From a83e112588b135070291736d0708d6827fc8eb81 Mon Sep 17 00:00:00 2001 From: Pranshu Date: Tue, 5 Mar 2024 03:59:20 +0530 Subject: [PATCH 01/21] Updated UI for Admin-> User Profile --- public/locales/en.json | 18 + src/GraphQl/Mutations/mutations.ts | 20 +- src/GraphQl/Queries/Queries.ts | 14 + src/components/Avatar/Avatar.tsx | 3 + .../MemberDetail/MemberDetail.module.css | 21 + src/screens/MemberDetail/MemberDetail.tsx | 693 +++++++++++++----- 6 files changed, 581 insertions(+), 188 deletions(-) diff --git a/public/locales/en.json b/public/locales/en.json index 14f57d86a9..7a69d13c2e 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -575,6 +575,24 @@ "firstName": "First name", "lastName": "Last name", "language": "Language", + "gender": "Gender", + "birthDate": "Birth Date", + "educationGrade": "Educational Grade", + "employmentStatus": "Employment Status", + "maritalStatus": "Marital Status", + "displayImage": "Display Image", + "phone": "Phone", + "address": "Address", + "countryCode": "Country Code", + "state": "State", + "city": "City", + "personalInfoHeading": "Personal Information", + "contactInfoHeading": "Contact Information", + "actionsHeading": "Actions", + "personalDetailsHeading": "Personal Details", + "appLanguageCode": "Choose Language", + "delete": "Delete User", + "saveChanges": "Save Changes", "adminApproved": "Admin approved", "pluginCreationAllowed": "Plugin creation allowed", "joined": "Joined", diff --git a/src/GraphQl/Mutations/mutations.ts b/src/GraphQl/Mutations/mutations.ts index 38170e187b..98bd9e6a31 100644 --- a/src/GraphQl/Mutations/mutations.ts +++ b/src/GraphQl/Mutations/mutations.ts @@ -87,10 +87,28 @@ export const UPDATE_USER_MUTATION = gql` $firstName: String $lastName: String $email: EmailAddress + $gender: Gender + $address: AddressInput + $birthDate: Date + $educationGrade: EducationGrade + $employmentStatus: EmploymentStatus + $maritalStatus: MaritalStatus + $phone: UserPhoneInput $file: String ) { updateUserProfile( - data: { firstName: $firstName, lastName: $lastName, email: $email } + data: { + firstName: $firstName + lastName: $lastName + email: $email + gender: $gender + address: $address + birthDate: $birthDate + educationGrade: $educationGrade + employmentStatus: $employmentStatus + maritalStatus: $maritalStatus + phone: $phone + } file: $file ) { _id diff --git a/src/GraphQl/Queries/Queries.ts b/src/GraphQl/Queries/Queries.ts index 573de80873..9aef498eac 100644 --- a/src/GraphQl/Queries/Queries.ts +++ b/src/GraphQl/Queries/Queries.ts @@ -440,6 +440,20 @@ export const USER_DETAILS = gql` pluginCreationAllowed adminApproved createdAt + gender + birthDate + educationGrade + employmentStatus + maritalStatus + address { + line1 + countryCode + city + state + } + phone { + home + } adminFor { _id } diff --git a/src/components/Avatar/Avatar.tsx b/src/components/Avatar/Avatar.tsx index 2cf5f7dda2..e967781d64 100644 --- a/src/components/Avatar/Avatar.tsx +++ b/src/components/Avatar/Avatar.tsx @@ -8,6 +8,7 @@ interface InterfaceAvatarProps { size?: number; avatarStyle?: string; dataTestId?: string; + radius?: number; } const Avatar = ({ @@ -16,11 +17,13 @@ const Avatar = ({ size, avatarStyle, dataTestId, + radius, }: InterfaceAvatarProps): JSX.Element => { const avatar = useMemo(() => { return createAvatar(initials, { size: size || 128, seed: name, + radius: radius || 0, }).toDataUriSync(); }, [name, size]); diff --git a/src/screens/MemberDetail/MemberDetail.module.css b/src/screens/MemberDetail/MemberDetail.module.css index 85246ed62b..93805e0400 100644 --- a/src/screens/MemberDetail/MemberDetail.module.css +++ b/src/screens/MemberDetail/MemberDetail.module.css @@ -448,3 +448,24 @@ .inactiveBtn:hover i { color: #31bb6b; } + +.topRadius { + border-top-left-radius: 16px; + border-top-right-radius: 16px; +} + +.inputColor { + background: #f1f3f6; +} + +.width60 { + width: 60%; +} + +.maxWidth40 { + max-width: 40%; +} + +.allRound { + border-radius: 16px; +} diff --git a/src/screens/MemberDetail/MemberDetail.tsx b/src/screens/MemberDetail/MemberDetail.tsx index 6b3b61f7df..49ce041bf6 100644 --- a/src/screens/MemberDetail/MemberDetail.tsx +++ b/src/screens/MemberDetail/MemberDetail.tsx @@ -14,12 +14,16 @@ import { languages } from 'utils/languages'; import { ADD_ADMIN_MUTATION, UPDATE_USERTYPE_MUTATION, + UPDATE_USER_MUTATION, } from 'GraphQl/Mutations/mutations'; import { toast } from 'react-toastify'; import { errorHandler } from 'utils/errorHandler'; import Loader from 'components/Loader/Loader'; import useLocalStorage from 'utils/useLocalstorage'; import Avatar from 'components/Avatar/Avatar'; +import { CalendarIcon } from '@mui/x-date-pickers'; +import { Form } from 'react-bootstrap'; +import convertToBase64 from 'utils/convertToBase64'; type MemberDetailProps = { id: string; // This is the userId @@ -38,22 +42,40 @@ const MemberDetail: React.FC = ({ const [isAdmin, setIsAdmin] = useState(false); const isMounted = useRef(true); - const { getItem } = useLocalStorage(); + const { getItem, setItem } = useLocalStorage(); const location = useLocation(); const currentUrl = location.state?.id || getItem('id') || id; const orgId = window.location.href.split('=')[1]; const calledFrom = location.state?.from || from; document.title = t('title'); + const [formState, setFormState] = useState({ + firstName: '', + lastName: '', + email: '', + applangcode: '', + file: '', + gender: '', + birthDate: '', + educationGrade: '', + employmentStatus: '', + maritalStatus: '', + phone: { + home: '', + }, + address: { + line1: '', + countryCode: '', + city: '', + state: '', + }, + pluginCreationAllowed: false, + adminApproved: false, + }); + const [adda] = useMutation(ADD_ADMIN_MUTATION); const [updateUserType] = useMutation(UPDATE_USERTYPE_MUTATION); - - useEffect(() => { - // check component is mounted or not - return () => { - isMounted.current = false; - }; - }, []); + const [updateUser] = useMutation(UPDATE_USER_MUTATION); const { data: userData, @@ -64,6 +86,134 @@ const MemberDetail: React.FC = ({ variables: { id: currentUrl }, // For testing we are sending the id as a prop }); + useEffect(() => { + if (userData) { + setFormState({ + ...formState, + firstName: userData?.user?.firstName, + lastName: userData?.user?.lastName, + email: userData?.user?.email, + applangcode: userData?.user?.applangcode, + gender: userData?.user?.gender, + birthDate: userData?.user?.birthDate, + educationGrade: userData?.user?.educationGrade, + employmentStatus: userData?.user?.employmentStatus, + maritalStatus: userData?.user?.maritalStatus, + phone: { + home: userData?.user?.phone?.home, + }, + address: { + line1: userData?.user?.address?.line1, + countryCode: userData?.user?.address?.countryCode, + city: userData?.user?.address?.city, + state: userData?.user?.address?.state, + }, + pluginCreationAllowed: userData?.user?.pluginCreationAllowed, + adminApproved: userData?.user?.adminApproved, + }); + } + }, [userData]); + + useEffect(() => { + // check component is mounted or not + return () => { + isMounted.current = false; + }; + }, []); + + const handleChange = (e: React.ChangeEvent): void => { + const { name, value } = e.target; + setFormState({ + ...formState, + [name]: value, + }); + console.log(formState); + }; + + const handleAddressChange = ( + e: React.ChangeEvent, + ): void => { + const { name, value } = e.target; + setFormState({ + ...formState, + address: { + ...formState.address, + [name]: value, + }, + }); + console.log(formState); + }; + + const handlePhoneChange = (e: React.ChangeEvent): void => { + const { name, value } = e.target; + setFormState({ + ...formState, + phone: { + ...formState.phone, + [name]: value, + }, + }); + console.log(formState); + }; + + const handleToggleChange = (e: React.ChangeEvent): void => { + const { name, checked } = e.target; + setFormState({ + ...formState, + [name]: checked, + }); + console.log(formState); + }; + + const loginLink = async (): Promise => { + try { + const firstName = formState.firstName; + const lastName = formState.lastName; + const email = formState.email; + const applangcode = formState.applangcode; + const file = formState.file; + const gender = formState.gender; + let toSubmit = true; + if (firstName.trim().length == 0 || !firstName) { + toast.warning('First Name cannot be blank!'); + toSubmit = false; + } + if (lastName.trim().length == 0 || !lastName) { + toast.warning('Last Name cannot be blank!'); + toSubmit = false; + } + if (email.trim().length == 0 || !email) { + toast.warning('Email cannot be blank!'); + toSubmit = false; + } + if (!toSubmit) return; + try { + const { data } = await updateUser({ + variables: { + //! Currently only some fields are supported by the api + id: currentUrl, + ...formState, + }, + }); + /* istanbul ignore next */ + if (data) { + if (getItem('id') === currentUrl) { + setItem('FirstName', firstName); + setItem('LastName', lastName); + setItem('Email', email); + setItem('UserImage', file); + } + toast.success('Successful updated'); + } + } catch (error: any) { + errorHandler(t, error); + } + } catch (error: any) { + /* istanbul ignore next */ + errorHandler(t, error); + } + }; + /* istanbul ignore next */ const toggleStateValue = (): void => { if (state === 1) setState(2); @@ -124,195 +274,344 @@ const MemberDetail: React.FC = ({ const memberDetails = ( - + {state == 1 ? ( -
- -

- {t('title')} -

-
- - - -
-
- - -
- {userData?.user?.image ? ( - - ) : ( - - )} -
- - - {/* User section */} -
-

- - {userData?.user?.firstName} {userData?.user?.lastName} - -

-

- {t('role')} :{' '} - {userData?.user?.userType} -

-

- {t('email')} :{' '} - {userData?.user?.email} -

-

- {t('createdOn')} :{' '} - {prettyDate(userData?.user?.createdAt)} -

+
+
+
+ {/* Personal */} +
+
+

{t('personalInfoHeading')}

+
+
+
+

{t('firstName')}

+ +
+
+

{t('lastName')}

+ +
+
+

{t('gender')}

+ +
+
+

{t('birthDate')}

+ +
+
+

{t('educationGrade')}

+ +
+
+

{t('employmentStatus')}

+ +
+
+

{t('maritalStatus')}

+ +
+

+ {t('displayImage')}: + => { + const target = e.target as HTMLInputElement; + const file = target.files && target.files[0]; + if (file) + setFormState({ + ...formState, + file: await convertToBase64(file), + }); + }} + data-testid="organisationImage" + /> +

+
- - -
-
-
- {/* Main Section And Activity section */} -
- - {/* Main Section */} - -
-
-
- {t('main')} -
+ {/* Contact Info */} +
+
+

{t('contactInfoHeading')}

+
+
+
+

{t('phone')}

+
-
- - {t('firstName')} - {userData?.user?.firstName} - - - {t('lastName')} - {userData?.user?.lastName} - - - {t('role')} - {userData?.user?.userType} - - - {t('language')} - - {getLanguageName(userData?.user?.appLanguageCode)} - - - - {t('adminApproved')} - - {userData?.user?.adminApproved ? 'Yes' : 'No'} - - - - {t('pluginCreationAllowed')} - - {userData?.user?.pluginCreationAllowed ? 'Yes' : 'No'} - - - - {t('createdOn')} - - {prettyDate(userData?.user?.createdAt)} - - +
+

{t('email')}

+
+
+

{t('address')}

+ +
+
+

{t('countryCode')}

+ +
+
+

{t('city')}

+ +
+
+

{t('state')}

+ +
+
+
+
+
+ {/* Personal */} +
+
+

{t('personalDetailsHeading')}

- - {/* Activity Section */} - - {/* Organizations */} -
-
-
- {t('organizations')} -
+
+
+ {userData?.user?.image ? ( + + ) : ( + <> + + + )} +
+

{userData?.user?.userType}

+
-
- - {t('created')} - - {userData?.user?.createdOrganizations?.length} - - - - {t('joined')} - - {userData?.user?.joinedOrganizations?.length} - - - - {t('adminForOrganizations')} - {userData?.user?.adminFor?.length} - - - {t('membershipRequests')} - - {userData?.user?.membershipRequests?.length} - - +
+

+ {formState?.firstName} +

+

{userData?.user?.email}

+

+ + Joined on {prettyDate(userData?.user?.createdAt)} +

- {/* Events */} -
-
-
- {t('events')} -
+
+ + {/* Actions */} +
+
+

{t('actionsHeading')}

+
+
+
+
+ +

+ {`${t('adminApproved')} (API not supported yet)`} +

+
+
+ +

+ {`${t('pluginCreationAllowed')} (API not supported yet)`} +

+
-
- - {t('created')} - - {userData?.user?.createdEvents?.length} - - - - {t('joined')} - - {userData?.user?.registeredEvents?.length} - - - - {t('adminForEvents')} - {userData?.user?.eventAdmin?.length} - +
+
+
+ +
+
+
+ + +
- - -
+
+
+ +
+
+
+ {/* Main Section And Activity section */}
) : ( @@ -321,6 +620,7 @@ const MemberDetail: React.FC = ({
); + console.log(userData); return ( <> {calledFrom === 'orglist' ? ( @@ -341,7 +641,26 @@ export const prettyDate = (param: string): string => { if (date?.toDateString() === 'Invalid Date') { return 'Unavailable'; } - return `${date?.toDateString()} ${date.toLocaleTimeString()}`; + const day = date.getDate(); + const month = date.toLocaleString('default', { month: 'long' }); + const year = date.getFullYear(); + return `${day}${getOrdinalSuffix(day)} ${month} ${year}`; +}; + +const getOrdinalSuffix = (day: number): string => { + if (day >= 11 && day <= 13) { + return 'th'; + } + switch (day % 10) { + case 1: + return 'st'; + case 2: + return 'nd'; + case 3: + return 'rd'; + default: + return 'th'; + } }; export const getLanguageName = (code: string): string => { From 628ff5a9f5549c080e2b67220af88a9abc978ca2 Mon Sep 17 00:00:00 2001 From: Pranshu Date: Tue, 5 Mar 2024 04:15:55 +0530 Subject: [PATCH 02/21] Added languages --- public/locales/fr.json | 18 ++++++++++++++++++ public/locales/hi.json | 18 ++++++++++++++++++ public/locales/sp.json | 18 ++++++++++++++++++ public/locales/zh.json | 18 ++++++++++++++++++ 4 files changed, 72 insertions(+) diff --git a/public/locales/fr.json b/public/locales/fr.json index 0b35b5098f..b4d5378fa2 100644 --- a/public/locales/fr.json +++ b/public/locales/fr.json @@ -570,6 +570,24 @@ "firstName": "Prénom", "lastName": "Nom de famille", "language": "Langue", + "gender": "Genre", + "birthDate": "Date de naissance", + "educationGrade": "Niveau d'éducation", + "employmentStatus": "Statut d'emploi", + "maritalStatus": "État civil", + "displayImage": "Image de profil", + "phone": "Téléphone", + "address": "Adresse", + "countryCode": "Code pays", + "state": "État", + "city": "Ville", + "personalInfoHeading": "Informations personnelles", + "contactInfoHeading": "Coordonnées", + "actionsHeading": "Actions", + "personalDetailsHeading": "Détails personnels", + "appLanguageCode": "Choisir la langue", + "delete": "Supprimer l'utilisateur", + "saveChanges": "Enregistrer les modifications", "adminApproved": "Approuvé par l'administrateur", "pluginCreationAllowed": "Autorisation de création de plugin", "joined": "Rejoint", diff --git a/public/locales/hi.json b/public/locales/hi.json index ee0f7242a3..f90df2887a 100644 --- a/public/locales/hi.json +++ b/public/locales/hi.json @@ -572,6 +572,24 @@ "firstName": "पहला नाम", "lastName": "अंतिम नाम", "language": "भाषा", + "gender": "लिंग", + "birthDate": "जन्म तिथि", + "educationGrade": "शैक्षिक ग्रेड", + "employmentStatus": "रोजगार की स्थिति", + "maritalStatus": "वैवाहिक स्थिति", + "displayImage": "प्रदर्शन छवि", + "phone": "फोन", + "address": "पता", + "countryCode": "देश कोड", + "state": "राज्य", + "city": "शहर", + "personalInfoHeading": "व्यक्तिगत जानकारी", + "contactInfoHeading": "संपर्क जानकारी", + "actionsHeading": "कार्रवाई", + "personalDetailsHeading": "व्यक्तिगत विवरण", + "appLanguageCode": "भाषा चुनें", + "delete": "उपयोगकर्ता को हटाएं", + "saveChanges": "परिवर्तन सहेजें", "adminApproved": "व्यवस्थापक द्वारा स्वीकृत", "pluginCreationAllowed": "प्लगइन निर्माण अनुमति दी गई", "joined": "शामिल हुए", diff --git a/public/locales/sp.json b/public/locales/sp.json index fa92fa1e6e..574a1cc6bc 100644 --- a/public/locales/sp.json +++ b/public/locales/sp.json @@ -570,6 +570,24 @@ "firstName": "Nombre", "lastName": "Apellido", "language": "Idioma", + "gender": "Género", + "birthDate": "Fecha de Nacimiento", + "educationGrade": "Nivel Educativo", + "employmentStatus": "Estado Laboral", + "maritalStatus": "Estado Civil", + "displayImage": "Imagen de Perfil", + "phone": "Teléfono", + "address": "Dirección", + "countryCode": "Código de País", + "state": "Estado", + "city": "Ciudad", + "personalInfoHeading": "Información Personal", + "contactInfoHeading": "Información de Contacto", + "actionsHeading": "Acciones", + "personalDetailsHeading": "Detalles Personales", + "appLanguageCode": "Elegir Idioma", + "delete": "Eliminar Usuario", + "saveChanges": "Guardar Cambios", "adminApproved": "Aprobado por el administrador", "pluginCreationAllowed": "Permitir creación de complementos", "joined": "Unido", diff --git a/public/locales/zh.json b/public/locales/zh.json index b588499ad5..c479997669 100644 --- a/public/locales/zh.json +++ b/public/locales/zh.json @@ -572,6 +572,24 @@ "firstName": "名字", "lastName": "姓氏", "language": "语言", + "gender": "性别", + "birthDate": "出生日期", + "educationGrade": "教育程度", + "employmentStatus": "就业状况", + "maritalStatus": "婚姻状况", + "displayImage": "显示图片", + "phone": "电话", + "address": "地址", + "countryCode": "国家代码", + "state": "州/省", + "city": "城市", + "personalInfoHeading": "个人信息", + "contactInfoHeading": "联系信息", + "actionsHeading": "操作", + "personalDetailsHeading": "个人详情", + "appLanguageCode": "选择语言", + "delete": "删除用户", + "saveChanges": "保存更改", "adminApproved": "管理员已批准", "pluginCreationAllowed": "允许创建插件", "joined": "加入", From ce541b64046a572966847f05532a8519f6bfbc83 Mon Sep 17 00:00:00 2001 From: Pranshu Date: Sat, 9 Mar 2024 12:52:37 +0530 Subject: [PATCH 03/21] Update localization strings for personal details --- package-lock.json | 103 ++++++------------ public/locales/en.json | 2 +- public/locales/fr.json | 2 +- public/locales/hi.json | 2 +- public/locales/sp.json | 2 +- .../MemberDetail/MemberDetail.test.tsx | 63 ++++++----- src/screens/MemberDetail/MemberDetail.tsx | 60 +--------- 7 files changed, 70 insertions(+), 164 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6cff468566..fbbbe70b78 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,7 +49,7 @@ "react-icons": "^4.12.0", "react-infinite-scroll-component": "^6.1.0", "react-redux": "^7.2.5", - "react-router-dom": "^6.21.3", + "react-router-dom": "^6.22.2", "react-scripts": "5.0.1", "react-toastify": "^9.0.3", "redux": "^4.1.1", @@ -4546,6 +4546,14 @@ "tslib": "^2.4.0" } }, + "node_modules/@remix-run/router": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", + "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@restart/hooks": { "version": "0.4.10", "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.10.tgz", @@ -11835,16 +11843,11 @@ } }, "node_modules/history": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", - "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz", + "integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==", "dependencies": { - "@babel/runtime": "^7.1.2", - "loose-envify": "^1.2.0", - "resolve-pathname": "^3.0.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0", - "value-equal": "^1.0.1" + "@babel/runtime": "^7.7.6" } }, "node_modules/hoist-non-react-statics": { @@ -12934,11 +12937,6 @@ "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", "dev": true }, - "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -17141,14 +17139,6 @@ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, - "node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dependencies": { - "isarray": "0.0.1" - } - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -19306,46 +19296,35 @@ } }, "node_modules/react-router": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", - "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", + "version": "6.22.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz", + "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==", "dependencies": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "hoist-non-react-statics": "^3.1.0", - "loose-envify": "^1.3.1", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.2", - "react-is": "^16.6.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" + "@remix-run/router": "1.15.3" + }, + "engines": { + "node": ">=14.0.0" }, "peerDependencies": { - "react": ">=15" + "react": ">=16.8" } }, "node_modules/react-router-dom": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.4.tgz", - "integrity": "sha512-m4EqFMHv/Ih4kpcBCONHbkT68KoAeHN4p3lAGoNryfHi0dMy0kCzEZakiKRsvg5wHZ/JLrLW8o8KomWiz/qbYQ==", + "version": "6.22.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz", + "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==", "dependencies": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "loose-envify": "^1.3.1", - "prop-types": "^15.6.2", - "react-router": "5.3.4", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" + "@remix-run/router": "1.15.3", + "react-router": "6.22.3" + }, + "engines": { + "node": ">=14.0.0" }, "peerDependencies": { - "react": ">=15" + "react": ">=16.8", + "react-dom": ">=16.8" } }, - "node_modules/react-router/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, "node_modules/react-scripts": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", @@ -20027,11 +20006,6 @@ "node": ">=4" } }, - "node_modules/resolve-pathname": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", - "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" - }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", @@ -21990,16 +21964,6 @@ "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==" }, - "node_modules/tiny-invariant": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", - "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" - }, - "node_modules/tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" - }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -22763,11 +22727,6 @@ "spdx-expression-parse": "^3.0.0" } }, - "node_modules/value-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", - "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/public/locales/en.json b/public/locales/en.json index e05f498ec2..820821afa5 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -627,7 +627,7 @@ "personalInfoHeading": "Personal Information", "contactInfoHeading": "Contact Information", "actionsHeading": "Actions", - "personalDetailsHeading": "Personal Details", + "personalDetailsHeading": "Profile Details", "appLanguageCode": "Choose Language", "delete": "Delete User", "saveChanges": "Save Changes", diff --git a/public/locales/fr.json b/public/locales/fr.json index 6178c201c6..e9e6801bf1 100644 --- a/public/locales/fr.json +++ b/public/locales/fr.json @@ -622,7 +622,7 @@ "personalInfoHeading": "Informations personnelles", "contactInfoHeading": "Coordonnées", "actionsHeading": "Actions", - "personalDetailsHeading": "Détails personnels", + "personalDetailsHeading": "Détails du profil", "appLanguageCode": "Choisir la langue", "delete": "Supprimer l'utilisateur", "saveChanges": "Enregistrer les modifications", diff --git a/public/locales/hi.json b/public/locales/hi.json index 59d1564344..6d805d237e 100644 --- a/public/locales/hi.json +++ b/public/locales/hi.json @@ -624,7 +624,7 @@ "personalInfoHeading": "व्यक्तिगत जानकारी", "contactInfoHeading": "संपर्क जानकारी", "actionsHeading": "कार्रवाई", - "personalDetailsHeading": "व्यक्तिगत विवरण", + "personalDetailsHeading": "प्रोफ़ाइल विवरण", "appLanguageCode": "भाषा चुनें", "delete": "उपयोगकर्ता को हटाएं", "saveChanges": "परिवर्तन सहेजें", diff --git a/public/locales/sp.json b/public/locales/sp.json index 0774eb9136..ff03ece6d3 100644 --- a/public/locales/sp.json +++ b/public/locales/sp.json @@ -623,7 +623,7 @@ "personalInfoHeading": "Información Personal", "contactInfoHeading": "Información de Contacto", "actionsHeading": "Acciones", - "personalDetailsHeading": "Detalles Personales", + "personalDetailsHeading": "Detalles del perfil", "appLanguageCode": "Elegir Idioma", "delete": "Eliminar Usuario", "saveChanges": "Guardar Cambios", diff --git a/src/screens/MemberDetail/MemberDetail.test.tsx b/src/screens/MemberDetail/MemberDetail.test.tsx index b26300be67..8f48b44cb2 100644 --- a/src/screens/MemberDetail/MemberDetail.test.tsx +++ b/src/screens/MemberDetail/MemberDetail.test.tsx @@ -45,6 +45,20 @@ const MOCKS1 = [ registeredEvents: [], eventAdmin: [], membershipRequests: [], + gender: 'MALE', + birthDate: '2023-02-18T09:22:27.969Z', + educationGrade: 'GRADE_A', + employmentStatus: 'EMPLOYED', + maritalStatus: 'SINGLE', + address: { + line1: 'abc', + countryCode: 'IN', + city: 'abc', + state: 'abc', + }, + phone: { + home: '1234567890', + }, }, }, }, @@ -109,6 +123,20 @@ const MOCKS2 = [ registeredEvents: [], eventAdmin: [], membershipRequests: [], + gender: 'MALE', + birthDate: '2023-02-18T09:22:27.969Z', + educationGrade: 'GRADE_A', + employmentStatus: 'EMPLOYED', + maritalStatus: 'SINGLE', + address: { + line1: 'abc', + countryCode: 'IN', + city: 'abc', + state: 'abc', + }, + phone: { + home: '1234567890', + }, }, }, }, @@ -161,41 +189,18 @@ describe('MemberDetail', () => { expect(screen.queryByText('Loading data...')).not.toBeInTheDocument(); await wait(); - - userEvent.click(screen.getByText(/Add Admin/i)); - - expect(screen.getByTestId('dashboardTitleBtn')).toBeInTheDocument(); - expect(screen.getByTestId('dashboardTitleBtn')).toHaveTextContent( - 'User Details', - ); expect(screen.getAllByText(/Email/i)).toBeTruthy(); - expect(screen.getAllByText(/Main/i)).toBeTruthy(); expect(screen.getAllByText(/First name/i)).toBeTruthy(); expect(screen.getAllByText(/Last name/i)).toBeTruthy(); expect(screen.getAllByText(/Language/i)).toBeTruthy(); expect(screen.getByText(/Admin approved/i)).toBeInTheDocument(); expect(screen.getByText(/Plugin creation allowed/i)).toBeInTheDocument(); - expect(screen.getAllByText(/Created on/i)).toBeTruthy(); - expect(screen.getAllByText(/Admin for organizations/i)).toBeTruthy(); - expect(screen.getAllByText(/Membership requests/i)).toBeTruthy(); - expect(screen.getAllByText(/Events/i)).toBeTruthy(); - expect(screen.getAllByText(/Admin for events/i)).toBeTruthy(); - - expect(screen.getAllByText(/Created On/i)).toHaveLength(2); - expect(screen.getAllByText(/User Details/i)).toHaveLength(1); - expect(screen.getAllByText(/Role/i)).toHaveLength(2); - expect(screen.getAllByText(/Created/i)).toHaveLength(4); - expect(screen.getAllByText(/Joined/i)).toHaveLength(2); - expect(screen.getByTestId('addAdminBtn')).toBeInTheDocument(); - const addAdminBtn = MOCKS1[2].request.variables.userType; - // if the button is not disabled - expect(screen.getByTestId('addAdminBtn').getAttribute('disabled')).toBe( - addAdminBtn == 'ADMIN' || addAdminBtn == 'SUPERADMIN' - ? expect.anything() - : null, - ); - expect(screen.getByTestId('stateBtn')).toBeInTheDocument(); - userEvent.click(screen.getByTestId('stateBtn')); + expect(screen.getAllByText(/Joined on/i)).toBeTruthy(); + expect(screen.getAllByText(/Joined On/i)).toHaveLength(1); + expect(screen.getAllByText(/Personal Information/i)).toHaveLength(1); + expect(screen.getAllByText(/Profile Details/i)).toHaveLength(1); + expect(screen.getAllByText(/Actions/i)).toHaveLength(1); + expect(screen.getAllByText(/Contact Information/i)).toHaveLength(1); }); test('prettyDate function should work properly', () => { diff --git a/src/screens/MemberDetail/MemberDetail.tsx b/src/screens/MemberDetail/MemberDetail.tsx index 6351340660..25e50dd2fe 100644 --- a/src/screens/MemberDetail/MemberDetail.tsx +++ b/src/screens/MemberDetail/MemberDetail.tsx @@ -225,50 +225,7 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { navigate(`/orgpeople/${currentUrl}`); } - const addAdmin = async (): Promise => { - try { - const { data } = await adda({ - variables: { - userid: location.state?.id, - orgid: orgId, - }, - }); - - /* istanbul ignore next */ - if (data) { - try { - const { data } = await updateUserType({ - variables: { - id: location.state?.id, - userType: 'ADMIN', - }, - }); - if (data) { - toast.success(t('addedAsAdmin')); - setTimeout(() => { - window.location.reload(); - }, 2000); - } - } catch (error: any) { - errorHandler(t, error); - } - } - } catch (error: any) { - /* istanbul ignore next */ - if ( - userData.user.userType === 'ADMIN' || - userData.user.userType === 'SUPERADMIN' - ) { - if (isMounted.current) setIsAdmin(true); - toast.error(t('alreadyIsAdmin')); - } else { - errorHandler(t, error); - } - } - }; - - - const memberDetails = ( + return ( {state == 1 ? ( @@ -615,21 +572,6 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { ); - - console.log(userData); - return ( - <> - {calledFrom === 'orglist' ? ( - - {memberDetails} - - ) : ( - - {memberDetails} - - )} - - ); }; export const prettyDate = (param: string): string => { From aa496b8b0b4452a1eb7da1c820317bc0afe2f1fe Mon Sep 17 00:00:00 2001 From: Pranshu Date: Sat, 9 Mar 2024 13:07:31 +0530 Subject: [PATCH 04/21] Add WidthFit class to MemberDetail module CSS --- src/screens/MemberDetail/MemberDetail.module.css | 4 ++++ src/screens/MemberDetail/MemberDetail.tsx | 10 +++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/screens/MemberDetail/MemberDetail.module.css b/src/screens/MemberDetail/MemberDetail.module.css index 93805e0400..f4b108620b 100644 --- a/src/screens/MemberDetail/MemberDetail.module.css +++ b/src/screens/MemberDetail/MemberDetail.module.css @@ -469,3 +469,7 @@ .allRound { border-radius: 16px; } + +.WidthFit { + width: fit-content; +} diff --git a/src/screens/MemberDetail/MemberDetail.tsx b/src/screens/MemberDetail/MemberDetail.tsx index 25e50dd2fe..53b3cc5408 100644 --- a/src/screens/MemberDetail/MemberDetail.tsx +++ b/src/screens/MemberDetail/MemberDetail.tsx @@ -453,14 +453,18 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { /> )} -
-

{userData?.user?.userType}

-

{formState?.firstName}

+
+

+ {userData?.user?.userType} +

+

{userData?.user?.email}

From b9bbccf68ce18de41e07893e81aff9b1c201bbb3 Mon Sep 17 00:00:00 2001 From: Pranshu Date: Sat, 9 Mar 2024 13:17:39 +0530 Subject: [PATCH 05/21] Add sanitize-html package and update MemberDetail component --- package-lock.json | 96 ++++++++++++++++++++++- package.json | 2 + src/screens/MemberDetail/MemberDetail.tsx | 10 ++- 3 files changed, 106 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index fbbbe70b78..3302521c65 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,6 +54,7 @@ "react-toastify": "^9.0.3", "redux": "^4.1.1", "redux-thunk": "^2.3.0", + "sanitize-html": "^2.12.1", "typedoc-plugin-markdown": "^3.17.1", "typescript": "^4.3.5", "web-vitals": "^1.0.1" @@ -74,6 +75,7 @@ "@types/react-dom": "^17.0.9", "@types/react-google-recaptcha": "^2.1.5", "@types/react-router-dom": "^5.1.8", + "@types/sanitize-html": "^2.11.0", "@typescript-eslint/eslint-plugin": "^5.9.0", "@typescript-eslint/parser": "^5.9.0", "cross-env": "^7.0.3", @@ -5704,6 +5706,15 @@ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" }, + "node_modules/@types/sanitize-html": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.11.0.tgz", + "integrity": "sha512-7oxPGNQHXLHE48r/r/qjn7q0hlrs3kL7oZnGj0Wf/h9tj/6ibFyRkNbsDxaBBZ4XUZ0Dx5LGCyDJ04ytSofacQ==", + "dev": true, + "dependencies": { + "htmlparser2": "^8.0.0" + } + }, "node_modules/@types/scheduler": { "version": "0.16.3", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", @@ -9198,6 +9209,19 @@ "csstype": "^3.0.2" } }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, "node_modules/domelementtype": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", @@ -9228,6 +9252,33 @@ "node": ">=8" } }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/dot-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", @@ -9363,7 +9414,6 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, "engines": { "node": ">=0.12" }, @@ -12042,6 +12092,24 @@ "webpack": "^5.20.0" } }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, "node_modules/http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", @@ -17093,6 +17161,11 @@ "node": ">=0.10.0" } }, + "node_modules/parse-srcset": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", + "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==" + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -20312,6 +20385,27 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/sanitize-html": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.12.1.tgz", + "integrity": "sha512-Plh+JAn0UVDpBRP/xEjsk+xDCoOvMBwQUf/K+/cBAVuTbtX8bj2VB7S1sL1dssVpykqp0/KPSesHrqXtokVBpA==", + "dependencies": { + "deepmerge": "^4.2.2", + "escape-string-regexp": "^4.0.0", + "htmlparser2": "^8.0.0", + "is-plain-object": "^5.0.0", + "parse-srcset": "^1.0.2", + "postcss": "^8.3.11" + } + }, + "node_modules/sanitize-html/node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/sanitize.css": { "version": "13.0.0", "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", diff --git a/package.json b/package.json index 1c677f770c..c1c19a8020 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "react-toastify": "^9.0.3", "redux": "^4.1.1", "redux-thunk": "^2.3.0", + "sanitize-html": "^2.12.1", "typedoc-plugin-markdown": "^3.17.1", "typescript": "^4.3.5", "web-vitals": "^1.0.1" @@ -106,6 +107,7 @@ "@types/react-dom": "^17.0.9", "@types/react-google-recaptcha": "^2.1.5", "@types/react-router-dom": "^5.1.8", + "@types/sanitize-html": "^2.11.0", "@typescript-eslint/eslint-plugin": "^5.9.0", "@typescript-eslint/parser": "^5.9.0", "cross-env": "^7.0.3", diff --git a/src/screens/MemberDetail/MemberDetail.tsx b/src/screens/MemberDetail/MemberDetail.tsx index 53b3cc5408..31ea6da7be 100644 --- a/src/screens/MemberDetail/MemberDetail.tsx +++ b/src/screens/MemberDetail/MemberDetail.tsx @@ -22,6 +22,7 @@ import Avatar from 'components/Avatar/Avatar'; import { CalendarIcon } from '@mui/x-date-pickers'; import { Form } from 'react-bootstrap'; import convertToBase64 from 'utils/convertToBase64'; +import sanitizeHtml from 'sanitize-html'; type MemberDetailProps = { id?: string; // This is the userId @@ -225,6 +226,13 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { navigate(`/orgpeople/${currentUrl}`); } + const sanitizedSrc = sanitizeHtml(formState.file, { + allowedTags: ['img'], + allowedAttributes: { + img: ['src', 'alt'], + }, + }); + return ( @@ -439,7 +447,7 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { ) : ( From 0ec4d5737f2d906eda11c9f0572653b9e2b587b3 Mon Sep 17 00:00:00 2001 From: Pranshu Date: Sun, 10 Mar 2024 03:30:54 +0530 Subject: [PATCH 06/21] lint fix --- src/screens/MemberDetail/MemberDetail.tsx | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/screens/MemberDetail/MemberDetail.tsx b/src/screens/MemberDetail/MemberDetail.tsx index 31ea6da7be..6be2be9ef1 100644 --- a/src/screens/MemberDetail/MemberDetail.tsx +++ b/src/screens/MemberDetail/MemberDetail.tsx @@ -4,16 +4,12 @@ import Col from 'react-bootstrap/Col'; import Row from 'react-bootstrap/Row'; import Button from 'react-bootstrap/Button'; import { useTranslation } from 'react-i18next'; -import { useParams, useLocation, useNavigate } from 'react-router-dom'; +import { useLocation, useNavigate } from 'react-router-dom'; import UserUpdate from 'components/UserUpdate/UserUpdate'; import { USER_DETAILS } from 'GraphQl/Queries/Queries'; import styles from './MemberDetail.module.css'; import { languages } from 'utils/languages'; -import { - ADD_ADMIN_MUTATION, - UPDATE_USERTYPE_MUTATION, - UPDATE_USER_MUTATION, -} from 'GraphQl/Mutations/mutations'; +import { UPDATE_USER_MUTATION } from 'GraphQl/Mutations/mutations'; import { toast } from 'react-toastify'; import { errorHandler } from 'utils/errorHandler'; import Loader from 'components/Loader/Loader'; @@ -36,13 +32,13 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { const location = useLocation(); const [state, setState] = useState(1); - const [isAdmin, setIsAdmin] = useState(false); + const isMounted = useRef(true); const { getItem, setItem } = useLocalStorage(); const currentUrl = location.state?.id || getItem('id') || id; - const { orgId } = useParams(); + document.title = t('title'); const [formState, setFormState] = useState({ @@ -69,8 +65,6 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { adminApproved: false, }); - const [adda] = useMutation(ADD_ADMIN_MUTATION); - const [updateUserType] = useMutation(UPDATE_USERTYPE_MUTATION); const [updateUser] = useMutation(UPDATE_USER_MUTATION); const { @@ -166,9 +160,9 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { const firstName = formState.firstName; const lastName = formState.lastName; const email = formState.email; - const applangcode = formState.applangcode; + // const applangcode = formState.applangcode; const file = formState.file; - const gender = formState.gender; + // const gender = formState.gender; let toSubmit = true; if (firstName.trim().length == 0 || !firstName) { toast.warning('First Name cannot be blank!'); From f92304f48be814bb22d392e55fcae197486c4e2a Mon Sep 17 00:00:00 2001 From: Pranshu Date: Sun, 10 Mar 2024 03:32:56 +0530 Subject: [PATCH 07/21] Merge branch 'admin-profile' of https://github.com/pranshugupta54/talawa-admin into admin-profile From eed9b6a4d54a6994e9045affdbf52253f57f84fd Mon Sep 17 00:00:00 2001 From: Pranshu Date: Sun, 10 Mar 2024 12:07:32 +0530 Subject: [PATCH 08/21] fix test - added fields to profile for test --- .../UserSidebar/UserSidebar.test.tsx | 28 +++++++++++++++++++ src/components/UserUpdate/UserUpdate.test.tsx | 14 ++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/components/UserPortal/UserSidebar/UserSidebar.test.tsx b/src/components/UserPortal/UserSidebar/UserSidebar.test.tsx index 1fb1ee72b5..10d5b4f133 100644 --- a/src/components/UserPortal/UserSidebar/UserSidebar.test.tsx +++ b/src/components/UserPortal/UserSidebar/UserSidebar.test.tsx @@ -47,6 +47,20 @@ const MOCKS = [ registeredEvents: [], eventAdmin: [], membershipRequests: [], + gender: 'MALE', + birthDate: '2023-02-18T09:22:27.969Z', + educationGrade: 'GRADE_A', + employmentStatus: 'EMPLOYED', + maritalStatus: 'SINGLE', + address: { + line1: 'abc', + countryCode: 'IN', + city: 'abc', + state: 'abc', + }, + phone: { + home: '1234567890', + }, }, }, }, @@ -80,6 +94,20 @@ const MOCKS = [ registeredEvents: [], eventAdmin: [], membershipRequests: [], + gender: 'MALE', + birthDate: '2023-02-18T09:22:27.969Z', + educationGrade: 'GRADE_A', + employmentStatus: 'EMPLOYED', + maritalStatus: 'SINGLE', + address: { + line1: 'abc', + countryCode: 'IN', + city: 'abc', + state: 'abc', + }, + phone: { + home: '1234567890', + }, }, }, }, diff --git a/src/components/UserUpdate/UserUpdate.test.tsx b/src/components/UserUpdate/UserUpdate.test.tsx index c9dd5d2472..8df1ae4ab0 100644 --- a/src/components/UserUpdate/UserUpdate.test.tsx +++ b/src/components/UserUpdate/UserUpdate.test.tsx @@ -41,6 +41,20 @@ const MOCKS = [ registeredEvents: [], eventAdmin: [], membershipRequests: [], + gender: '', + birthDate: '', + educationGrade: '', + employmentStatus: '', + maritalStatus: '', + address: { + line1: '', + countryCode: '', + city: '', + state: '', + }, + phone: { + home: '', + }, }, }, }, From 7fbd34bbc5b12f863ca528d250eaf15ea62b8567 Mon Sep 17 00:00:00 2001 From: Pranshu Date: Sun, 10 Mar 2024 16:41:40 +0530 Subject: [PATCH 09/21] Added placeholded, dynamicDropdown --- .../DynamicDropDown.module.css | 11 ++ .../DynamicDropDown/DynamicDropDown.tsx | 55 +++++++ .../MemberDetail/MemberDetail.module.css | 40 +++++ src/screens/MemberDetail/MemberDetail.tsx | 105 +++++++----- src/utils/memberFields.ts | 149 ++++++++++++++++++ 5 files changed, 322 insertions(+), 38 deletions(-) create mode 100644 src/components/DynamicDropDown/DynamicDropDown.module.css create mode 100644 src/components/DynamicDropDown/DynamicDropDown.tsx create mode 100644 src/utils/memberFields.ts diff --git a/src/components/DynamicDropDown/DynamicDropDown.module.css b/src/components/DynamicDropDown/DynamicDropDown.module.css new file mode 100644 index 0000000000..9d2d73cc6b --- /dev/null +++ b/src/components/DynamicDropDown/DynamicDropDown.module.css @@ -0,0 +1,11 @@ +.dropwdownToggle { + background-color: #f1f3f6; + color: black; + border: none; + padding: 0.5rem; + text-align: left; + display: flex; + align-items: center; + justify-content: space-between; + min-width: 8rem; +} diff --git a/src/components/DynamicDropDown/DynamicDropDown.tsx b/src/components/DynamicDropDown/DynamicDropDown.tsx new file mode 100644 index 0000000000..170b6be29b --- /dev/null +++ b/src/components/DynamicDropDown/DynamicDropDown.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { Dropdown } from 'react-bootstrap'; +import styles from './DynamicDropDown.module.css'; + +interface InterfaceChangeDropDownProps { + parentContainerStyle?: string; + btnStyle?: string; + btnTextStyle?: string; + setFormState: React.Dispatch>; + formState: any; + fieldOptions: { value: string; label: string }[]; // Field options for dropdown + fieldName: string; // Field name for labeling +} + +const DynamicDropDown = (props: InterfaceChangeDropDownProps): JSX.Element => { + const handleFieldChange = (value: string): void => { + props.setFormState({ ...props.formState, [props.fieldName]: value }); + }; + + const getLabel = (value: string): string => { + const selectedOption = props.fieldOptions.find( + (option) => option.value === value, + ); + return selectedOption ? selectedOption.label : `None`; + }; + + return ( + + + {getLabel(props.formState[props.fieldName])} + + + {props.fieldOptions.map((option, index: number) => ( + handleFieldChange(option.value)} + data-testid={`change-${props.fieldName.toLowerCase()}-btn-${option.value}`} + > + {option.label} + + ))} + + + ); +}; + +export default DynamicDropDown; diff --git a/src/screens/MemberDetail/MemberDetail.module.css b/src/screens/MemberDetail/MemberDetail.module.css index f4b108620b..91c641a03b 100644 --- a/src/screens/MemberDetail/MemberDetail.module.css +++ b/src/screens/MemberDetail/MemberDetail.module.css @@ -473,3 +473,43 @@ .WidthFit { width: fit-content; } + +.datebox { + border-radius: 7px; + border-color: #e8e5e5; + outline: none; + box-shadow: none; + padding-top: 2px; + padding-bottom: 2px; + padding-right: 5px; + padding-left: 5px; + margin-right: 5px; + margin-left: 5px; +} + +.datebox > div > input { + padding: 0.5rem 0 0.5rem 0.5rem !important; /* top, right, bottom, left */ + background-color: #f1f3f6; + border-radius: var(--bs-border-radius) !important; + border: none !important; +} + +.datebox > div > div { + margin-left: 0px !important; +} + +.datebox > div > fieldset { + border: none !important; + /* background-color: #f1f3f6; */ + border-radius: var(--bs-border-radius) !important; +} + +.datebox > div { + margin: 0.5rem !important; + background-color: #f1f3f6; +} + +input::file-selector-button { + background-color: black; + color: white; +} diff --git a/src/screens/MemberDetail/MemberDetail.tsx b/src/screens/MemberDetail/MemberDetail.tsx index 6be2be9ef1..49ef56cdc6 100644 --- a/src/screens/MemberDetail/MemberDetail.tsx +++ b/src/screens/MemberDetail/MemberDetail.tsx @@ -15,10 +15,19 @@ import { errorHandler } from 'utils/errorHandler'; import Loader from 'components/Loader/Loader'; import useLocalStorage from 'utils/useLocalstorage'; import Avatar from 'components/Avatar/Avatar'; -import { CalendarIcon } from '@mui/x-date-pickers'; +import { CalendarIcon, DatePicker } from '@mui/x-date-pickers'; import { Form } from 'react-bootstrap'; import convertToBase64 from 'utils/convertToBase64'; import sanitizeHtml from 'sanitize-html'; +import type { Dayjs } from 'dayjs'; +import dayjs from 'dayjs'; +import { + educationGradeEnum, + maritalStatusEnum, + genderEnum, + employmentStatusEnum, +} from 'utils/memberFields'; +import DynamicDropDown from 'components/DynamicDropDown/DynamicDropDown'; type MemberDetailProps = { id?: string; // This is the userId @@ -65,6 +74,21 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { adminApproved: false, }); + // Handle date change + const handleDateChange = (date: Dayjs | null): void => { + if (date) { + setFormState((prevState) => ({ + ...prevState, + birthDate: dayjs(date).format('YYYY-MM-DD'), // Convert Dayjs object to JavaScript Date object + })); + } else { + setFormState((prevState) => ({ + ...prevState, + birthDate: '', // Set birthDate to null if no date is selected + })); + } + }; + const [updateUser] = useMutation(UPDATE_USER_MUTATION); const { @@ -252,6 +276,7 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { id="" onChange={handleChange} required + placeholder="John" />

@@ -264,61 +289,59 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { id="" onChange={handleChange} required + placeholder="Doe" />

{t('gender')}

- +
+ {/* */} + +

{t('birthDate')}

- +
+ +

{t('educationGrade')}

-

{t('employmentStatus')}

-

{t('maritalStatus')}

-

@@ -376,6 +399,8 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { name="email" id="" onChange={handleChange} + required + placeholder="john@example.com" />

@@ -387,6 +412,7 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { name="line1" id="" onChange={handleAddressChange} + placeholder="123 Random Street" />
@@ -398,6 +424,7 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { name="countryCode" id="" onChange={handleAddressChange} + placeholder="eg. US or IN" />
@@ -409,6 +436,7 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { name="city" id="" onChange={handleAddressChange} + placeholder="Queens" />
@@ -420,6 +448,7 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { name="state" id="" onChange={handleAddressChange} + placeholder="NYC" />
diff --git a/src/utils/memberFields.ts b/src/utils/memberFields.ts new file mode 100644 index 0000000000..440dbfcf4c --- /dev/null +++ b/src/utils/memberFields.ts @@ -0,0 +1,149 @@ +const educationGradeEnum = [ + { + value: 'NO_GRADE', + label: 'No Grade', + }, + { + value: 'PRE_KG', + label: 'Pre-KG', + }, + { + value: 'KG', + label: 'KG', + }, + { + value: 'GRADE_1', + label: 'Grade 1', + }, + { + value: 'GRADE_2', + label: 'Grade 2', + }, + { + value: 'GRADE_3', + label: 'Grade 3', + }, + { + value: 'GRADE_4', + label: 'Grade 4', + }, + { + value: 'GRADE_5', + label: 'Grade 5', + }, + { + value: 'GRADE_6', + label: 'Grade 6', + }, + { + value: 'GRADE_7', + label: 'Grade 7', + }, + { + value: 'GRADE_8', + label: 'Grade 8', + }, + { + value: 'GRADE_9', + label: 'Grade 9', + }, + { + value: 'GRADE_10', + label: 'Grade 10', + }, + { + value: 'GRADE_11', + label: 'Grade 11', + }, + { + value: 'GRADE_12', + label: 'Grade 12', + }, + { + value: 'GRADUATE', + label: 'Graduate', + }, +]; + +const maritalStatusEnum = [ + { + value: 'SINGLE', + label: 'Single', + }, + { + value: 'ENGAGED', + label: 'Engaged', + }, + { + value: 'MARRIED', + label: 'Married', + }, + { + value: 'DIVORCED', + label: 'Divorced', + }, + { + value: 'WIDOWED', + label: 'Widowed', + }, + { + value: 'SEPARATED', + label: 'Separated', + }, +]; + +const genderEnum = [ + { + value: 'MALE', + label: 'Male', + }, + { + value: 'FEMALE', + label: 'Female', + }, + { + value: 'OTHER', + label: 'Other', + }, +]; + +const employmentStatusEnum = [ + { + value: 'FULL_TIME', + label: 'Full Time', + }, + { + value: 'PART_TIME', + label: 'Part Time', + }, + { + value: 'UNEMPLOYED', + label: 'Unemployed', + }, +]; +const userTypeEnum = [ + { + value: 'USER', + label: 'User', + }, + { + value: 'ADMIN', + label: 'Admin', + }, + { + value: 'SUPERADMIN', + label: 'Super Admin', + }, + { + value: 'NON_USER', + label: 'Non-User', + }, +]; + +export { + educationGradeEnum, + maritalStatusEnum, + genderEnum, + employmentStatusEnum, + userTypeEnum, +}; From 97c77af59f44c862a43ca3af1570e62325769ba2 Mon Sep 17 00:00:00 2001 From: Pranshu Date: Mon, 11 Mar 2024 11:10:23 +0530 Subject: [PATCH 10/21] Wrapped the component in LocalizationProvider of dayjs --- src/screens/MemberDetail/MemberDetail.tsx | 637 +++++++++++----------- 1 file changed, 324 insertions(+), 313 deletions(-) diff --git a/src/screens/MemberDetail/MemberDetail.tsx b/src/screens/MemberDetail/MemberDetail.tsx index 49ef56cdc6..6003ccad2d 100644 --- a/src/screens/MemberDetail/MemberDetail.tsx +++ b/src/screens/MemberDetail/MemberDetail.tsx @@ -15,7 +15,12 @@ import { errorHandler } from 'utils/errorHandler'; import Loader from 'components/Loader/Loader'; import useLocalStorage from 'utils/useLocalstorage'; import Avatar from 'components/Avatar/Avatar'; -import { CalendarIcon, DatePicker } from '@mui/x-date-pickers'; +import { + CalendarIcon, + DatePicker, + LocalizationProvider, +} from '@mui/x-date-pickers'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import { Form } from 'react-bootstrap'; import convertToBase64 from 'utils/convertToBase64'; import sanitizeHtml from 'sanitize-html'; @@ -252,360 +257,366 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { }); return ( - - - {state == 1 ? ( -
-
-
- {/* Personal */} -
+ + + + {state == 1 ? ( +
+
+
+ {/* Personal */}
-

{t('personalInfoHeading')}

-
-
-
-

{t('firstName')}

- +
+

{t('personalInfoHeading')}

-
-

{t('lastName')}

- -
-
-

{t('gender')}

-
- {/* +
+

{t('firstName')}

+ +
+
+

{t('lastName')}

+ +
+
+

{t('gender')}

+
+ {/* */} + +
+
+
+

{t('birthDate')}

+
+ +
+
+
+

{t('educationGrade')}

-
-
-

{t('birthDate')}

- {t('employmentStatus')}

+
+
+

{t('maritalStatus')}

+ +
+

+ {t('displayImage')}: + => { + const target = e.target as HTMLInputElement; + const file = target.files && target.files[0]; + if (file) + setFormState({ + ...formState, + file: await convertToBase64(file), + }); + }} + data-testid="organisationImage" + /> +

-
-

{t('educationGrade')}

- -
-
-

{t('employmentStatus')}

- -
-
-

{t('maritalStatus')}

- -
-

- {t('displayImage')}: - => { - const target = e.target as HTMLInputElement; - const file = target.files && target.files[0]; - if (file) - setFormState({ - ...formState, - file: await convertToBase64(file), - }); - }} - data-testid="organisationImage" - /> -

-
- {/* Contact Info */} -
+ {/* Contact Info */}
-

{t('contactInfoHeading')}

-
-
-
-

{t('phone')}

- +
+

{t('contactInfoHeading')}

-
-

{t('email')}

- -
-
-

{t('address')}

- -
-
-

{t('countryCode')}

- -
-
-

{t('city')}

- -
-
-

{t('state')}

- +
+
+

{t('phone')}

+ +
+
+

{t('email')}

+ +
+
+

{t('address')}

+ +
+
+

{t('countryCode')}

+ +
+
+

{t('city')}

+ +
+
+

{t('state')}

+ +
-
-
- {/* Personal */} -
+
+ {/* Personal */}
-

{t('personalDetailsHeading')}

-
-
-
- {userData?.user?.image ? ( - - ) : ( - <> - - - )} +
+

{t('personalDetailsHeading')}

-
-

- {formState?.firstName} -

-
-

- {userData?.user?.userType} +

+
+ {userData?.user?.image ? ( + + ) : ( + <> + + + )} +
+
+

+ {formState?.firstName} +

+
+

+ {userData?.user?.userType} +

+
+

{userData?.user?.email}

+

+ + Joined on {prettyDate(userData?.user?.createdAt)}

-

{userData?.user?.email}

-

- - Joined on {prettyDate(userData?.user?.createdAt)} -

-
- {/* Actions */} -
+ {/* Actions */}
-

{t('actionsHeading')}

-
-
-
-
- -

- {`${t('adminApproved')} (API not supported yet)`} -

-
-
- -

- {`${t('pluginCreationAllowed')} (API not supported yet)`} -

-
+
+

{t('actionsHeading')}

-
-
-
-
-
-
- +
+ +
+ {/* Main Section And Activity section */}
- {/* Main Section And Activity section */} -
- ) : ( - - )} - - + ) : ( + + )} + + + ); }; From 8077780dbd8d492edfb26af32c39cd000a364502 Mon Sep 17 00:00:00 2001 From: Pranshu Date: Tue, 12 Mar 2024 21:41:53 +0530 Subject: [PATCH 11/21] remove unused lines --- src/screens/MemberDetail/MemberDetail.tsx | 673 ++++++++++------------ 1 file changed, 305 insertions(+), 368 deletions(-) diff --git a/src/screens/MemberDetail/MemberDetail.tsx b/src/screens/MemberDetail/MemberDetail.tsx index 6003ccad2d..992b0232f1 100644 --- a/src/screens/MemberDetail/MemberDetail.tsx +++ b/src/screens/MemberDetail/MemberDetail.tsx @@ -1,11 +1,8 @@ import React, { useEffect, useRef, useState } from 'react'; import { useMutation, useQuery } from '@apollo/client'; -import Col from 'react-bootstrap/Col'; -import Row from 'react-bootstrap/Row'; import Button from 'react-bootstrap/Button'; import { useTranslation } from 'react-i18next'; import { useLocation, useNavigate } from 'react-router-dom'; -import UserUpdate from 'components/UserUpdate/UserUpdate'; import { USER_DETAILS } from 'GraphQl/Queries/Queries'; import styles from './MemberDetail.module.css'; import { languages } from 'utils/languages'; @@ -44,17 +41,10 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { }); const navigate = useNavigate(); const location = useLocation(); - - const [state, setState] = useState(1); - const isMounted = useRef(true); - const { getItem, setItem } = useLocalStorage(); - const currentUrl = location.state?.id || getItem('id') || id; - document.title = t('title'); - const [formState, setFormState] = useState({ firstName: '', lastName: '', @@ -78,7 +68,6 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { pluginCreationAllowed: false, adminApproved: false, }); - // Handle date change const handleDateChange = (date: Dayjs | null): void => { if (date) { @@ -93,9 +82,7 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { })); } }; - const [updateUser] = useMutation(UPDATE_USER_MUTATION); - const { data: userData, loading: loading, @@ -224,6 +211,7 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { } toast.success('Successful updated'); } + refetch(); } catch (error: any) { errorHandler(t, error); } @@ -233,13 +221,6 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { } }; - /* istanbul ignore next */ - const toggleStateValue = (): void => { - if (state === 1) setState(2); - else setState(1); - refetch(); - }; - if (loading) { return ; } @@ -258,368 +239,327 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { return ( - - - {state == 1 ? ( -
-
-
- {/* Personal */} -
-
-

{t('personalInfoHeading')}

-
-
-
-

{t('firstName')}

- -
-
-

{t('lastName')}

- -
-
-

{t('gender')}

-
- {/* */} - -
-
-
-

{t('birthDate')}

-
- -
-
-
-

{t('educationGrade')}

- -
-
-

{t('employmentStatus')}

- -
-
-

{t('maritalStatus')}

- -
-

- {t('displayImage')}: - => { - const target = e.target as HTMLInputElement; - const file = target.files && target.files[0]; - if (file) - setFormState({ - ...formState, - file: await convertToBase64(file), - }); - }} - data-testid="organisationImage" - /> -

-
+
+
+
+ {/* Personal */} +
+
+

{t('personalInfoHeading')}

+
+
+
+

{t('firstName')}

+ +
+
+

{t('lastName')}

+ +
+
+

{t('gender')}

+
+
- {/* Contact Info */} -
-
-

{t('contactInfoHeading')}

-
-
-
-

{t('phone')}

- -
-
-

{t('email')}

- -
-
-

{t('address')}

- -
-
-

{t('countryCode')}

- -
-
-

{t('city')}

- -
-
-

{t('state')}

- -
-
+
+
+

{t('birthDate')}

+
+
-
- {/* Personal */} +
+

{t('educationGrade')}

+ +
+
+

{t('employmentStatus')}

+ +
+
+

{t('maritalStatus')}

+ +
+

+ {t('displayImage')}: + => { + const target = e.target as HTMLInputElement; + const file = target.files && target.files[0]; + if (file) + setFormState({ + ...formState, + file: await convertToBase64(file), + }); + }} + data-testid="organisationImage" + /> +

+
+
+ {/* Contact Info */} +
+
+

{t('contactInfoHeading')}

+
+
+
+

{t('phone')}

+ +
+
+

{t('email')}

+ +
+
+

{t('address')}

+ +
+
+

{t('countryCode')}

+ +
+
+

{t('city')}

+ +
+
+

{t('state')}

+ +
+
+
+
+
+ {/* Personal */} +
+
+

{t('personalDetailsHeading')}

+
+
+
+ {userData?.user?.image ? ( + + ) : ( + <> + + + )} +
+
+

{formState?.firstName}

-
-

{t('personalDetailsHeading')}

-
-
-
- {userData?.user?.image ? ( - - ) : ( - <> - - - )} -
-
-

- {formState?.firstName} -

-
-

- {userData?.user?.userType} -

-
-

{userData?.user?.email}

-

- - Joined on {prettyDate(userData?.user?.createdAt)} -

-
-
+

{userData?.user?.userType}

+

{userData?.user?.email}

+

+ + Joined on {prettyDate(userData?.user?.createdAt)} +

+
+
+
- {/* Actions */} -
-
-

{t('actionsHeading')}

-
-
-
-
- -

- {`${t('adminApproved')} (API not supported yet)`} -

-
-
- -

- {`${t('pluginCreationAllowed')} (API not supported yet)`} -

-
-
-
-
-
- -
-
-
- - -
-
+ {/* Actions */} +
+
+

{t('actionsHeading')}

+
+
+
+
+ +

+ {`${t('adminApproved')} (API not supported yet)`} +

+
+
+ +

+ {`${t('pluginCreationAllowed')} (API not supported yet)`} +

+
+
+
+
+
+
-
-
- {/* Main Section And Activity section */}
- ) : ( - - )} - - +
+ +
+
+
+
); }; - export const prettyDate = (param: string): string => { const date = new Date(param); if (date?.toDateString() === 'Invalid Date') { @@ -630,7 +570,6 @@ export const prettyDate = (param: string): string => { const year = date.getFullYear(); return `${day}${getOrdinalSuffix(day)} ${month} ${year}`; }; - const getOrdinalSuffix = (day: number): string => { if (day >= 11 && day <= 13) { return 'th'; @@ -646,7 +585,6 @@ const getOrdinalSuffix = (day: number): string => { return 'th'; } }; - export const getLanguageName = (code: string): string => { let language = 'Unavailable'; languages.map((data) => { @@ -656,5 +594,4 @@ export const getLanguageName = (code: string): string => { }); return language; }; - export default MemberDetail; From d07b83d32a39defac7b3243a5fd8935756c3dba0 Mon Sep 17 00:00:00 2001 From: Pranshu Date: Fri, 22 Mar 2024 22:31:39 +0530 Subject: [PATCH 12/21] Add new translations and update MemberDetail component --- public/locales/en.json | 7 +- .../MemberDetail/MemberDetail.test.tsx | 125 ++++++++++++++++++ src/screens/MemberDetail/MemberDetail.tsx | 54 ++++---- 3 files changed, 159 insertions(+), 27 deletions(-) diff --git a/public/locales/en.json b/public/locales/en.json index 04e08c54d8..b4df845889 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -677,7 +677,12 @@ "membershipRequests": "Membership requests", "adminForEvents": "Admin for events", "addedAsAdmin": "User is added as admin.", - "talawaApiUnavailable": "Talawa-API service is unavailable. Kindly check your network connection and wait for a while." + "talawaApiUnavailable": "Talawa-API service is unavailable. Kindly check your network connection and wait for a while.", + "password": "Password", + "userType": "User Type", + "admin": "Admin", + "superAdmin": "Superadmin", + "cancel": "Cancel" }, "userLogin": { "login": "Login", diff --git a/src/screens/MemberDetail/MemberDetail.test.tsx b/src/screens/MemberDetail/MemberDetail.test.tsx index 8f48b44cb2..45d7bec7db 100644 --- a/src/screens/MemberDetail/MemberDetail.test.tsx +++ b/src/screens/MemberDetail/MemberDetail.test.tsx @@ -9,12 +9,84 @@ import { I18nextProvider } from 'react-i18next'; import { ADD_ADMIN_MUTATION, UPDATE_USERTYPE_MUTATION, + UPDATE_USER_MUTATION, } from 'GraphQl/Mutations/mutations'; import { USER_DETAILS } from 'GraphQl/Queries/Queries'; import i18nForTest from 'utils/i18nForTest'; import { StaticMockLink } from 'utils/StaticMockLink'; import MemberDetail, { getLanguageName, prettyDate } from './MemberDetail'; +const MOCKS = [ + { + request: { + query: USER_DETAILS, + variables: { + id: '1', + }, + }, + result: { + data: { + user: { + __typename: 'User', + image: null, + firstName: '', + lastName: '', + email: '', + role: 'SUPERADMIN', + appLanguageCode: 'en', + userType: 'SUPERADMIN', + pluginCreationAllowed: true, + adminApproved: true, + createdAt: '2023-02-18T09:22:27.969Z', + adminFor: [], + createdOrganizations: [], + joinedOrganizations: [], + organizationsBlockedBy: [], + createdEvents: [], + registeredEvents: [], + eventAdmin: [], + membershipRequests: [], + gender: '', + birthDate: '', + educationGrade: '', + employmentStatus: '', + maritalStatus: '', + address: { + line1: '', + countryCode: '', + city: '', + state: '', + }, + phone: { + home: '', + }, + }, + }, + }, + }, + { + request: { + query: UPDATE_USER_MUTATION, + variable: { + firstName: '', + lastName: '', + email: '', + }, + }, + result: { + data: { + users: [ + { + _id: '1', + }, + ], + }, + }, + }, +]; + +const link = new StaticMockLink(MOCKS, true); + const MOCKS1 = [ { request: { @@ -221,6 +293,59 @@ describe('MemberDetail', () => { expect(getLangName('')).toBe('Unavailable'); }); + test('should render props and text elements test for the page component', async () => { + const props = { + key: '123', + id: '1', + toggleStateValue: jest.fn(), + }; + + const formData = { + firstName: 'Ansh', + lastName: 'Goyal', + email: 'ansh@gmail.com', + image: new File(['hello'], 'hello.png', { type: 'image/png' }), + }; + render( + + + + + + + , + ); + + await wait(); + + userEvent.type( + screen.getByPlaceholderText(/First Name/i), + formData.firstName, + ); + userEvent.type( + screen.getByPlaceholderText(/Last Name/i), + formData.lastName, + ); + userEvent.type(screen.getByPlaceholderText(/Email/i), formData.email); + userEvent.selectOptions(screen.getByTestId('applangcode'), 'Français'); + userEvent.upload(screen.getByLabelText(/Display Image:/i), formData.image); + await wait(); + + userEvent.click(screen.getByText(/Save Changes/i)); + + expect(screen.getByPlaceholderText(/First Name/i)).toHaveValue( + formData.firstName, + ); + expect(screen.getByPlaceholderText(/Last Name/i)).toHaveValue( + formData.lastName, + ); + expect(screen.getByPlaceholderText(/Email/i)).toHaveValue(formData.email); + expect(screen.getByPlaceholderText(/First Name/i)).toBeInTheDocument(); + expect(screen.getByPlaceholderText(/Last Name/i)).toBeInTheDocument(); + expect(screen.getByPlaceholderText(/Email/i)).toBeInTheDocument(); + expect(screen.getByText(/Display Image/i)).toBeInTheDocument(); + }); + test('Should display dicebear image if image is null', async () => { const props = { id: 'rishav-jha-mech', diff --git a/src/screens/MemberDetail/MemberDetail.tsx b/src/screens/MemberDetail/MemberDetail.tsx index 992b0232f1..ba31967e00 100644 --- a/src/screens/MemberDetail/MemberDetail.tsx +++ b/src/screens/MemberDetail/MemberDetail.tsx @@ -259,7 +259,7 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { name="firstName" onChange={handleChange} required - placeholder="John" + placeholder={t('firstName')} />
@@ -271,7 +271,7 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { name="lastName" onChange={handleChange} required - placeholder="Doe" + placeholder={t('lastName')} />
@@ -323,25 +323,27 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { />

- {t('displayImage')}: - => { - const target = e.target as HTMLInputElement; - const file = target.files && target.files[0]; - if (file) - setFormState({ - ...formState, - file: await convertToBase64(file), - }); - }} - data-testid="organisationImage" - /> +

@@ -372,7 +374,7 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { name="email" onChange={handleChange} required - placeholder="john@example.com" + placeholder={t('email')} />
@@ -383,7 +385,7 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { type="email" name="line1" onChange={handleAddressChange} - placeholder="123 Random Street" + placeholder={t('address')} />
@@ -394,7 +396,7 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { type="text" name="countryCode" onChange={handleAddressChange} - placeholder="eg. US or IN" + placeholder={t('countryCode')} />
@@ -405,7 +407,7 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { type="text" name="city" onChange={handleAddressChange} - placeholder="Queens" + placeholder={t('city')} />
@@ -416,7 +418,7 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { type="text" name="state" onChange={handleAddressChange} - placeholder="NYC" + placeholder={t('state')} />
From 728660f273e1731dbcaf64404d3d3e5755a19e97 Mon Sep 17 00:00:00 2001 From: Pranshu Date: Fri, 22 Mar 2024 22:56:28 +0530 Subject: [PATCH 13/21] Add toast warnings for blank form submission --- .../MemberDetail/MemberDetail.test.tsx | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/screens/MemberDetail/MemberDetail.test.tsx b/src/screens/MemberDetail/MemberDetail.test.tsx index 45d7bec7db..b934da733f 100644 --- a/src/screens/MemberDetail/MemberDetail.test.tsx +++ b/src/screens/MemberDetail/MemberDetail.test.tsx @@ -15,6 +15,7 @@ import { USER_DETAILS } from 'GraphQl/Queries/Queries'; import i18nForTest from 'utils/i18nForTest'; import { StaticMockLink } from 'utils/StaticMockLink'; import MemberDetail, { getLanguageName, prettyDate } from './MemberDetail'; +import { toast } from 'react-toastify'; const MOCKS = [ { @@ -327,6 +328,8 @@ describe('MemberDetail', () => { formData.lastName, ); userEvent.type(screen.getByPlaceholderText(/Email/i), formData.email); + // userEvent.click(screen.getByTestId('gender-dropdown-btn')); + // userEvent.click(screen.getByTestId('change-gender-btn-FEMALE')); userEvent.selectOptions(screen.getByTestId('applangcode'), 'Français'); userEvent.upload(screen.getByLabelText(/Display Image:/i), formData.image); await wait(); @@ -346,6 +349,33 @@ describe('MemberDetail', () => { expect(screen.getByText(/Display Image/i)).toBeInTheDocument(); }); + test('should display warnings for blank form submission', async () => { + jest.spyOn(toast, 'warning'); + const props = { + key: '123', + id: '1', + toggleStateValue: jest.fn(), + }; + + render( + + + + + + + , + ); + + await wait(); + + userEvent.click(screen.getByText(/Save Changes/i)); + + expect(toast.warning).toHaveBeenCalledWith('First Name cannot be blank!'); + expect(toast.warning).toHaveBeenCalledWith('Last Name cannot be blank!'); + expect(toast.warning).toHaveBeenCalledWith('Email cannot be blank!'); + }); + test('Should display dicebear image if image is null', async () => { const props = { id: 'rishav-jha-mech', From 53632d0ca009e5d736e1a0d4728a9e889e20a027 Mon Sep 17 00:00:00 2001 From: Pranshu Date: Fri, 22 Mar 2024 23:55:03 +0530 Subject: [PATCH 14/21] Update MemberDetail.test.tsx with additional form fields and birth date --- .../MemberDetail/MemberDetail.test.tsx | 41 ++++++++++++-- src/screens/MemberDetail/MemberDetail.tsx | 53 ++++++++----------- 2 files changed, 59 insertions(+), 35 deletions(-) diff --git a/src/screens/MemberDetail/MemberDetail.test.tsx b/src/screens/MemberDetail/MemberDetail.test.tsx index b934da733f..4c26553226 100644 --- a/src/screens/MemberDetail/MemberDetail.test.tsx +++ b/src/screens/MemberDetail/MemberDetail.test.tsx @@ -1,5 +1,11 @@ import React from 'react'; -import { act, render, screen, waitFor } from '@testing-library/react'; +import { + act, + fireEvent, + render, + screen, + waitFor, +} from '@testing-library/react'; import { MockedProvider } from '@apollo/react-testing'; import userEvent from '@testing-library/user-event'; import { BrowserRouter } from 'react-router-dom'; @@ -48,7 +54,7 @@ const MOCKS = [ eventAdmin: [], membershipRequests: [], gender: '', - birthDate: '', + birthDate: '03/28/2023', educationGrade: '', employmentStatus: '', maritalStatus: '', @@ -237,6 +243,14 @@ async function wait(ms = 2): Promise { await act(() => new Promise((resolve) => setTimeout(resolve, ms))); } +jest.mock('@mui/x-date-pickers/DateTimePicker', () => { + return { + DateTimePicker: jest.requireActual( + '@mui/x-date-pickers/DesktopDateTimePicker', + ).DesktopDateTimePicker, + }; +}); + jest.mock('react-toastify'); describe('MemberDetail', () => { @@ -306,6 +320,12 @@ describe('MemberDetail', () => { lastName: 'Goyal', email: 'ansh@gmail.com', image: new File(['hello'], 'hello.png', { type: 'image/png' }), + address: 'abc', + countryCode: 'IN', + state: 'abc', + city: 'abc', + phone: '1234567890', + birthDate: '03/28/2022', }; render( @@ -318,6 +338,10 @@ describe('MemberDetail', () => { ); await wait(); + const birthDateDatePicker = screen.getByTestId('birthDate'); + fireEvent.change(birthDateDatePicker, { + target: { value: formData.birthDate }, + }); userEvent.type( screen.getByPlaceholderText(/First Name/i), @@ -327,9 +351,17 @@ describe('MemberDetail', () => { screen.getByPlaceholderText(/Last Name/i), formData.lastName, ); + userEvent.type(screen.getByPlaceholderText(/Address/i), formData.address); + userEvent.type( + screen.getByPlaceholderText(/Country Code/i), + formData.countryCode, + ); + userEvent.type(screen.getByPlaceholderText(/State/i), formData.state); + userEvent.type(screen.getByPlaceholderText(/City/i), formData.city); userEvent.type(screen.getByPlaceholderText(/Email/i), formData.email); - // userEvent.click(screen.getByTestId('gender-dropdown-btn')); - // userEvent.click(screen.getByTestId('change-gender-btn-FEMALE')); + userEvent.type(screen.getByPlaceholderText(/Phone/i), formData.phone); + userEvent.click(screen.getByPlaceholderText(/adminApproved/i)); + userEvent.click(screen.getByPlaceholderText(/pluginCreationAllowed/i)); userEvent.selectOptions(screen.getByTestId('applangcode'), 'Français'); userEvent.upload(screen.getByLabelText(/Display Image:/i), formData.image); await wait(); @@ -342,6 +374,7 @@ describe('MemberDetail', () => { expect(screen.getByPlaceholderText(/Last Name/i)).toHaveValue( formData.lastName, ); + expect(birthDateDatePicker).toHaveValue(formData.birthDate); expect(screen.getByPlaceholderText(/Email/i)).toHaveValue(formData.email); expect(screen.getByPlaceholderText(/First Name/i)).toBeInTheDocument(); expect(screen.getByPlaceholderText(/Last Name/i)).toBeInTheDocument(); diff --git a/src/screens/MemberDetail/MemberDetail.tsx b/src/screens/MemberDetail/MemberDetail.tsx index ba31967e00..aea2623d2a 100644 --- a/src/screens/MemberDetail/MemberDetail.tsx +++ b/src/screens/MemberDetail/MemberDetail.tsx @@ -75,11 +75,6 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { ...prevState, birthDate: dayjs(date).format('YYYY-MM-DD'), // Convert Dayjs object to JavaScript Date object })); - } else { - setFormState((prevState) => ({ - ...prevState, - birthDate: '', // Set birthDate to null if no date is selected - })); } }; const [updateUser] = useMutation(UPDATE_USER_MUTATION); @@ -133,7 +128,7 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { ...formState, [name]: value, }); - console.log(formState); + // console.log(formState); }; const handleAddressChange = ( @@ -147,7 +142,7 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { [name]: value, }, }); - console.log(formState); + // console.log(formState); }; const handlePhoneChange = (e: React.ChangeEvent): void => { @@ -159,16 +154,17 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { [name]: value, }, }); - console.log(formState); + // console.log(formState); }; const handleToggleChange = (e: React.ChangeEvent): void => { + console.log(e.target.checked); const { name, checked } = e.target; setFormState({ ...formState, [name]: checked, }); - console.log(formState); + // console.log(formState); }; const loginLink = async (): Promise => { @@ -211,7 +207,6 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { } toast.success('Successful updated'); } - refetch(); } catch (error: any) { errorHandler(t, error); } @@ -289,9 +284,18 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => {

{t('birthDate')}

@@ -363,6 +367,7 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { type="number" name="home" onChange={handlePhoneChange} + placeholder={t('phone')} />
@@ -486,8 +491,9 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { name="adminApproved" className="mx-2" checked={formState.adminApproved} - onChange={handleToggleChange} - disabled // API not supporting this feature + onChange={handleToggleChange} // API not supporting this feature + data-testid="adminApproved" + placeholder="adminApproved" />

{`${t('adminApproved')} (API not supported yet)`} @@ -499,8 +505,9 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { name="pluginCreationAllowed" className="mx-2" checked={formState.pluginCreationAllowed} - onChange={handleToggleChange} - disabled // API not supporting this feature + onChange={handleToggleChange} // API not supporting this feature + data-testid="pluginCreationAllowed" + placeholder="pluginCreationAllowed" />

{`${t('pluginCreationAllowed')} (API not supported yet)`} @@ -514,7 +521,6 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { {t('appLanguageCode')}
{`(API not supported yet)`}

{t('countryCode')}

{t('city')}

{t('state')}

@@ -440,11 +429,11 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => {
- {userData?.user?.image ? ( + {formState.image ? ( ) : ( @@ -464,9 +453,15 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => {
-

{userData?.user?.userType}

+

+ {userData?.appUserProfile?.isSuperAdmin + ? 'Super Admin' + : userData?.appUserProfile?.adminFor.length > 0 + ? 'Admin' + : 'User'} +

-

{userData?.user?.email}

+

{formState.email}

Joined on {prettyDate(userData?.user?.createdAt)} @@ -488,7 +483,7 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { = ({ id }): JSX.Element => { { const day = date.getDate(); const month = date.toLocaleString('default', { month: 'long' }); const year = date.getFullYear(); - return `${day}} ${month} ${year}`; + return `${day} ${month} ${year}`; }; export const getLanguageName = (code: string): string => { let language = 'Unavailable'; @@ -586,4 +581,4 @@ export const getLanguageName = (code: string): string => { }); return language; }; -export default MemberDetail; \ No newline at end of file +export default MemberDetail; From aa245d103ad3b3fd205fe28c7160d7bbb76f3249 Mon Sep 17 00:00:00 2001 From: Pranshu Date: Wed, 27 Mar 2024 01:38:40 +0530 Subject: [PATCH 18/21] Refactor MemberDetail component --- package-lock.json | 39 +- src/GraphQl/Queries/Queries.ts | 4 +- .../MemberDetail/MemberDetail.test.tsx | 354 +++++++++--------- src/screens/MemberDetail/MemberDetail.tsx | 106 +++--- 4 files changed, 268 insertions(+), 235 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8ea799a0ec..79fd79fd84 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4526,14 +4526,6 @@ "tslib": "^2.4.0" } }, - "node_modules/@remix-run/router": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", - "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==", - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@remix-run/router": { "version": "1.15.2", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.2.tgz", @@ -8557,6 +8549,37 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/css-select/node_modules/dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dependencies": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "node_modules/css-select/node_modules/domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dependencies": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "node_modules/css-select/node_modules/domutils/node_modules/domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "node_modules/css-select/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/css-select/node_modules/nth-check": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", diff --git a/src/GraphQl/Queries/Queries.ts b/src/GraphQl/Queries/Queries.ts index 646ed4331c..59af216bff 100644 --- a/src/GraphQl/Queries/Queries.ts +++ b/src/GraphQl/Queries/Queries.ts @@ -470,7 +470,7 @@ export const USER_DETAILS = gql` gender maritalStatus phone { - home + mobile } address { line1 @@ -492,6 +492,8 @@ export const USER_DETAILS = gql` _id } isSuperAdmin + appLanguageCode + pluginCreationAllowed createdOrganizations { _id } diff --git a/src/screens/MemberDetail/MemberDetail.test.tsx b/src/screens/MemberDetail/MemberDetail.test.tsx index 4c26553226..6bd069cd25 100644 --- a/src/screens/MemberDetail/MemberDetail.test.tsx +++ b/src/screens/MemberDetail/MemberDetail.test.tsx @@ -23,77 +23,6 @@ import { StaticMockLink } from 'utils/StaticMockLink'; import MemberDetail, { getLanguageName, prettyDate } from './MemberDetail'; import { toast } from 'react-toastify'; -const MOCKS = [ - { - request: { - query: USER_DETAILS, - variables: { - id: '1', - }, - }, - result: { - data: { - user: { - __typename: 'User', - image: null, - firstName: '', - lastName: '', - email: '', - role: 'SUPERADMIN', - appLanguageCode: 'en', - userType: 'SUPERADMIN', - pluginCreationAllowed: true, - adminApproved: true, - createdAt: '2023-02-18T09:22:27.969Z', - adminFor: [], - createdOrganizations: [], - joinedOrganizations: [], - organizationsBlockedBy: [], - createdEvents: [], - registeredEvents: [], - eventAdmin: [], - membershipRequests: [], - gender: '', - birthDate: '03/28/2023', - educationGrade: '', - employmentStatus: '', - maritalStatus: '', - address: { - line1: '', - countryCode: '', - city: '', - state: '', - }, - phone: { - home: '', - }, - }, - }, - }, - }, - { - request: { - query: UPDATE_USER_MUTATION, - variable: { - firstName: '', - lastName: '', - email: '', - }, - }, - result: { - data: { - users: [ - { - _id: '1', - }, - ], - }, - }, - }, -]; - -const link = new StaticMockLink(MOCKS, true); - const MOCKS1 = [ { request: { @@ -105,38 +34,87 @@ const MOCKS1 = [ result: { data: { user: { - __typename: 'User', - image: null, - firstName: 'Rishav', - lastName: 'Jha', - email: 'ris@gmail.com', - role: 'SUPERADMIN', - appLanguageCode: 'en', - userType: 'SUPERADMIN', - pluginCreationAllowed: true, - adminApproved: true, - createdAt: '2023-02-18T09:22:27.969Z', - adminFor: [], - createdOrganizations: [], - joinedOrganizations: [], - organizationsBlockedBy: [], - createdEvents: [], - registeredEvents: [], - eventAdmin: [], - membershipRequests: [], - gender: 'MALE', - birthDate: '2023-02-18T09:22:27.969Z', - educationGrade: 'GRADE_A', - employmentStatus: 'EMPLOYED', - maritalStatus: 'SINGLE', - address: { - line1: 'abc', - countryCode: 'IN', - city: 'abc', - state: 'abc', + __typename: 'UserData', + appUserProfile: { + _id: '1', + __typename: 'AppUserProfile', + adminFor: [ + { + __typename: 'Organization', + _id: '65e0df0906dd1228350cfd4a', + }, + { + __typename: 'Organization', + _id: '65e0e2abb92c9f3e29503d4e', + }, + ], + isSuperAdmin: true, + appLanguageCode: 'en', + createdEvents: [ + { + __typename: 'Event', + _id: '65e32a5b2a1f4288ca1f086a', + }, + ], + createdOrganizations: [ + { + __typename: 'Organization', + _id: '65e0df0906dd1228350cfd4a', + }, + { + __typename: 'Organization', + _id: '65e0e2abb92c9f3e29503d4e', + }, + ], + eventAdmin: [ + { + __typename: 'Event', + _id: '65e32a5b2a1f4288ca1f086a', + }, + ], + pluginCreationAllowed: true, + adminApproved: true, }, - phone: { - home: '1234567890', + user: { + _id: '1', + __typename: 'User', + createdAt: '2024-02-26T10:36:33.098Z', + email: 'adi790u@gmail.com', + firstName: 'Aditya', + image: null, + lastName: 'Agarwal', + gender: '', + birthDate: '2024-03-14', + educationGrade: '', + employmentStatus: '', + maritalStatus: '', + address: { + line1: '', + countryCode: '', + city: '', + state: '', + }, + phone: { + mobile: '', + }, + joinedOrganizations: [ + { + __typename: 'Organization', + _id: '65e0df0906dd1228350cfd4a', + }, + { + __typename: 'Organization', + _id: '65e0e2abb92c9f3e29503d4e', + }, + ], + membershipRequests: [], + organizationsBlockedBy: [], + registeredEvents: [ + { + __typename: 'Event', + _id: '65e32a5b2a1f4288ca1f086a', + }, + ], }, }, }, @@ -156,20 +134,6 @@ const MOCKS1 = [ }, }, }, - { - request: { - query: UPDATE_USERTYPE_MUTATION, - variables: { - id: '123', - userType: 'Admin', - }, - }, - result: { - data: { - success: true, - }, - }, - }, ]; const MOCKS2 = [ @@ -183,38 +147,87 @@ const MOCKS2 = [ result: { data: { user: { - __typename: 'User', - image: 'https://placeholder.com/200x200', - firstName: 'Rishav', - lastName: 'Jha', - email: 'ris@gmail.com', - role: 'SUPERADMIN', - appLanguageCode: 'en', - userType: 'SUPERADMIN', - pluginCreationAllowed: false, - adminApproved: false, - createdAt: '2023-02-18T09:22:27.969Z', - adminFor: [], - createdOrganizations: [], - joinedOrganizations: [], - organizationsBlockedBy: [], - createdEvents: [], - registeredEvents: [], - eventAdmin: [], - membershipRequests: [], - gender: 'MALE', - birthDate: '2023-02-18T09:22:27.969Z', - educationGrade: 'GRADE_A', - employmentStatus: 'EMPLOYED', - maritalStatus: 'SINGLE', - address: { - line1: 'abc', - countryCode: 'IN', - city: 'abc', - state: 'abc', + __typename: 'UserData', + appUserProfile: { + _id: '1', + __typename: 'AppUserProfile', + adminFor: [ + { + __typename: 'Organization', + _id: '65e0df0906dd1228350cfd4a', + }, + { + __typename: 'Organization', + _id: '65e0e2abb92c9f3e29503d4e', + }, + ], + isSuperAdmin: true, + appLanguageCode: 'en', + createdEvents: [ + { + __typename: 'Event', + _id: '65e32a5b2a1f4288ca1f086a', + }, + ], + createdOrganizations: [ + { + __typename: 'Organization', + _id: '65e0df0906dd1228350cfd4a', + }, + { + __typename: 'Organization', + _id: '65e0e2abb92c9f3e29503d4e', + }, + ], + eventAdmin: [ + { + __typename: 'Event', + _id: '65e32a5b2a1f4288ca1f086a', + }, + ], + pluginCreationAllowed: true, + adminApproved: true, }, - phone: { - home: '1234567890', + user: { + _id: '1', + __typename: 'User', + createdAt: '2024-02-26T10:36:33.098Z', + email: 'adi790u@gmail.com', + firstName: 'Aditya', + image: 'https://placeholder.com/200x200', + lastName: 'Agarwal', + gender: '', + birthDate: '2024-03-14', + educationGrade: '', + employmentStatus: '', + maritalStatus: '', + address: { + line1: '', + countryCode: '', + city: '', + state: '', + }, + phone: { + mobile: '', + }, + joinedOrganizations: [ + { + __typename: 'Organization', + _id: '65e0df0906dd1228350cfd4a', + }, + { + __typename: 'Organization', + _id: '65e0e2abb92c9f3e29503d4e', + }, + ], + membershipRequests: [], + organizationsBlockedBy: [], + registeredEvents: [ + { + __typename: 'Event', + _id: '65e32a5b2a1f4288ca1f086a', + }, + ], }, }, }, @@ -239,7 +252,7 @@ const MOCKS2 = [ const link1 = new StaticMockLink(MOCKS1, true); const link2 = new StaticMockLink(MOCKS2, true); -async function wait(ms = 2): Promise { +async function wait(ms = 20): Promise { await act(() => new Promise((resolve) => setTimeout(resolve, ms))); } @@ -310,9 +323,7 @@ describe('MemberDetail', () => { test('should render props and text elements test for the page component', async () => { const props = { - key: '123', id: '1', - toggleStateValue: jest.fn(), }; const formData = { @@ -324,20 +335,23 @@ describe('MemberDetail', () => { countryCode: 'IN', state: 'abc', city: 'abc', - phone: '1234567890', + phoneNumber: '1234567890', birthDate: '03/28/2022', }; render( - - - - - - + + + + + + + + , ); - + expect(screen.queryByText('Loading data...')).not.toBeInTheDocument(); await wait(); + expect(screen.getAllByText(/Email/i)).toBeTruthy(); const birthDateDatePicker = screen.getByTestId('birthDate'); fireEvent.change(birthDateDatePicker, { target: { value: formData.birthDate }, @@ -359,7 +373,7 @@ describe('MemberDetail', () => { userEvent.type(screen.getByPlaceholderText(/State/i), formData.state); userEvent.type(screen.getByPlaceholderText(/City/i), formData.city); userEvent.type(screen.getByPlaceholderText(/Email/i), formData.email); - userEvent.type(screen.getByPlaceholderText(/Phone/i), formData.phone); + userEvent.type(screen.getByPlaceholderText(/Phone/i), formData.phoneNumber); userEvent.click(screen.getByPlaceholderText(/adminApproved/i)); userEvent.click(screen.getByPlaceholderText(/pluginCreationAllowed/i)); userEvent.selectOptions(screen.getByTestId('applangcode'), 'Français'); @@ -391,12 +405,14 @@ describe('MemberDetail', () => { }; render( - - - - - - + + + + + + + + , ); @@ -455,7 +471,7 @@ describe('MemberDetail', () => { expect(screen.queryByText('Loading data...')).not.toBeInTheDocument(); - const user = MOCKS2[0].result.data.user; + const user = MOCKS2[0].result?.data?.user?.user; const userImage = await screen.findByTestId('userImagePresent'); expect(userImage).toBeInTheDocument(); expect(userImage.getAttribute('src')).toBe(user?.image); @@ -466,7 +482,7 @@ describe('MemberDetail', () => { id: 'rishav-jha-mech', }; render( - + @@ -487,7 +503,7 @@ describe('MemberDetail', () => { id: 'rishav-jha-mech', }; render( - + @@ -524,7 +540,7 @@ describe('MemberDetail', () => { }); test('should be redirected to / if member id is undefined', async () => { render( - + diff --git a/src/screens/MemberDetail/MemberDetail.tsx b/src/screens/MemberDetail/MemberDetail.tsx index 6f84d5e48c..aee32e0eb6 100644 --- a/src/screens/MemberDetail/MemberDetail.tsx +++ b/src/screens/MemberDetail/MemberDetail.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useRef, useState } from 'react'; import { useMutation, useQuery } from '@apollo/client'; import Button from 'react-bootstrap/Button'; import { useTranslation } from 'react-i18next'; -import { useLocation, useNavigate } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; import { USER_DETAILS } from 'GraphQl/Queries/Queries'; import styles from './MemberDetail.module.css'; import { languages } from 'utils/languages'; @@ -39,7 +39,6 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { const { t } = useTranslation('translation', { keyPrefix: 'memberDetail', }); - const navigate = useNavigate(); const location = useLocation(); const isMounted = useRef(true); const { getItem, setItem } = useLocalStorage(); @@ -49,16 +48,14 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { firstName: '', lastName: '', email: '', - applangcode: '', + appLanguageCode: '', image: '', gender: '', - birthDate: '', + birthDate: '2024-03-14', grade: '', empStatus: '', maritalStatus: '', - phone: { - home: '', - }, + phoneNumber: '', address: '', state: '', city: '', @@ -76,47 +73,37 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { } }; const [updateUser] = useMutation(UPDATE_USER_MUTATION); - const { - data: user, - loading: loading, - error: error, - } = useQuery(USER_DETAILS, { + const { data: user, loading: loading } = useQuery(USER_DETAILS, { variables: { id: currentUrl }, // For testing we are sending the id as a prop }); const userData = user?.user; useEffect(() => { - if (userData) { - console.log(userData); + if (userData && isMounted) { + // console.log(userData); setFormState({ ...formState, firstName: userData?.user?.firstName, lastName: userData?.user?.lastName, email: userData?.user?.email, - applangcode: userData?.user?.applangcode, + appLanguageCode: userData?.appUserProfile?.appLanguageCode, gender: userData?.user?.gender, - birthDate: userData?.user?.birthDate, + birthDate: userData?.user?.birthDate || '2020-03-14', grade: userData?.user?.educationGrade, empStatus: userData?.user?.employmentStatus, maritalStatus: userData?.user?.maritalStatus, - phone: { - home: userData?.user?.phone?.home, - }, + phoneNumber: userData?.user?.phone?.mobile, address: userData.user?.address?.line1, state: userData?.user?.address?.state, city: userData?.user?.address?.city, country: userData?.user?.address?.countryCode, - pluginCreationAllowed: userData?.user?.pluginCreationAllowed, - adminApproved: userData?.user?.adminApproved, + pluginCreationAllowed: userData?.appUserProfile?.pluginCreationAllowed, + adminApproved: userData?.appUserProfile?.adminApproved, image: userData?.user?.image || '', }); } }, [userData, user]); - useEffect(() => { - console.log(formState); - }, [formState]); - useEffect(() => { // check component is mounted or not return () => { @@ -126,41 +113,47 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { const handleChange = (e: React.ChangeEvent): void => { const { name, value } = e.target; - setFormState({ - ...formState, + // setFormState({ + // ...formState, + // [name]: value, + // }); + // console.log(name, value); + setFormState((prevState) => ({ + ...prevState, [name]: value, - }); + })); // console.log(formState); }; - const handlePhoneChange = (e: React.ChangeEvent): void => { - const { name, value } = e.target; - setFormState({ - ...formState, - phone: { - ...formState.phone, - [name]: value, - }, - }); - // console.log(formState); - }; + // const handlePhoneChange = (e: React.ChangeEvent): void => { + // const { name, value } = e.target; + // setFormState({ + // ...formState, + // phoneNumber: { + // ...formState.phoneNumber, + // [name]: value, + // }, + // }); + // // console.log(formState); + // }; const handleToggleChange = (e: React.ChangeEvent): void => { - console.log(e.target.checked); + // console.log(e.target.checked); const { name, checked } = e.target; - setFormState({ - ...formState, + setFormState((prevState) => ({ + ...prevState, [name]: checked, - }); + })); // console.log(formState); }; const loginLink = async (): Promise => { try { + console.log(formState); const firstName = formState.firstName; const lastName = formState.lastName; const email = formState.email; - // const applangcode = formState.applangcode; + // const appLanguageCode = formState.appLanguageCode; const image = formState.image; // const gender = formState.gender; let toSubmit = true; @@ -195,12 +188,16 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { } toast.success('Successful updated'); } - } catch (error: any) { - errorHandler(t, error); + } catch (error: unknown) { + if (error instanceof Error) { + errorHandler(t, error); + } } - } catch (error: any) { + } catch (error: unknown) { /* istanbul ignore next */ - errorHandler(t, error); + if (error instanceof Error) { + errorHandler(t, error); + } } }; @@ -208,11 +205,6 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { return ; } - /* istanbul ignore next */ - if (error) { - navigate(`/orgpeople/${currentUrl}`); - } - const sanitizedSrc = sanitizeHtml(formState.image, { allowedTags: ['img'], allowedAttributes: { @@ -350,11 +342,11 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => {

{t('phone')}

@@ -520,7 +512,7 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { onChange={(e): void => { setFormState({ ...formState, - applangcode: e.target.value, + appLanguageCode: e.target.value, }); }} > From 2f661129fe8e4973762fe08264459bf67b2063b8 Mon Sep 17 00:00:00 2001 From: Pranshu Gupta Date: Wed, 27 Mar 2024 01:43:49 +0530 Subject: [PATCH 19/21] Update pre-commit --- .husky/pre-commit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index bcb4e32a5f..c9c109cceb 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -3,7 +3,7 @@ # npm run format:fix # npm run lint:fix -# npm run lint-staged +npm run lint-staged npm run typecheck npm run update:toc From 525bffa72739a61b6f20dab55cf24e76f00b316e Mon Sep 17 00:00:00 2001 From: Pranshu Date: Wed, 27 Mar 2024 02:04:29 +0530 Subject: [PATCH 20/21] remove unused UserUpdate --- .../UserUpdate/UserUpdate.module.css | 97 ------- src/components/UserUpdate/UserUpdate.test.tsx | 196 ------------- src/components/UserUpdate/UserUpdate.tsx | 269 ------------------ src/screens/MemberDetail/MemberDetail.tsx | 2 +- 4 files changed, 1 insertion(+), 563 deletions(-) delete mode 100644 src/components/UserUpdate/UserUpdate.module.css delete mode 100644 src/components/UserUpdate/UserUpdate.test.tsx delete mode 100644 src/components/UserUpdate/UserUpdate.tsx diff --git a/src/components/UserUpdate/UserUpdate.module.css b/src/components/UserUpdate/UserUpdate.module.css deleted file mode 100644 index fb9b781dcb..0000000000 --- a/src/components/UserUpdate/UserUpdate.module.css +++ /dev/null @@ -1,97 +0,0 @@ -/* .userupdatediv{ - border: 1px solid #e8e5e5; - box-shadow: 2px 1px #e8e5e5; - padding:25px 16px; - border-radius: 5px; - background:#fdfdfd; -} */ -.settingstitle { - color: #707070; - font-size: 20px; - margin-bottom: 30px; - text-align: center; - margin-top: -10px; -} -.dispflex { - display: flex; - flex-direction: column; - justify-content: flex-start; - margin: 0 8rem; -} - -.dispflex > div { - width: 100%; - margin-right: 50px; -} - -.radio_buttons > input { - margin-bottom: 20px; - border: none; - box-shadow: none; - padding: 0 0; - border-radius: 5px; - background: none; - width: 50%; -} - -.dispbtnflex { - margin-top: 20px; - display: flex; - justify-content: center; -} - -.whitebtn { - margin: 1rem 0 0; - margin-top: 10px; - border: 1px solid #e8e5e5; - box-shadow: 0 2px 2px #e8e5e5; - padding: 10px 20px; - border-radius: 5px; - background: none; - font-size: 16px; - color: #31bb6b; - outline: none; - font-weight: 600; - cursor: pointer; - float: left; - transition: - transform 0.2s, - box-shadow 0.2s; -} -.greenregbtn { - margin: 1rem 0 0; - margin-top: 10px; - margin-right: 30px; - border: 1px solid #e8e5e5; - box-shadow: 0 2px 2px #e8e5e5; - padding: 10px 10px; - border-radius: 5px; - background-color: #31bb6b; - font-size: 16px; - color: white; - outline: none; - font-weight: 600; - cursor: pointer; - transition: - transform 0.2s, - box-shadow 0.2s; -} -.radio_buttons { - width: 55%; - margin-top: 10px; - display: flex; - color: #707070; - font-weight: 600; - font-size: 14px; -} -.radio_buttons > input { - transform: scale(1.2); -} -.radio_buttons > label { - margin-top: -4px; - margin-left: 0px; - margin-right: 7px; -} -.idtitle { - width: 88%; -} diff --git a/src/components/UserUpdate/UserUpdate.test.tsx b/src/components/UserUpdate/UserUpdate.test.tsx deleted file mode 100644 index c7d7a511e6..0000000000 --- a/src/components/UserUpdate/UserUpdate.test.tsx +++ /dev/null @@ -1,196 +0,0 @@ -import React from 'react'; -import { act, render, screen } from '@testing-library/react'; -import { MockedProvider } from '@apollo/react-testing'; -import userEvent from '@testing-library/user-event'; -import { I18nextProvider } from 'react-i18next'; -import { BrowserRouter as Router } from 'react-router-dom'; -import UserUpdate from './UserUpdate'; -import { UPDATE_USER_MUTATION } from 'GraphQl/Mutations/mutations'; -import i18nForTest from 'utils/i18nForTest'; -import { USER_DETAILS } from 'GraphQl/Queries/Queries'; -import { StaticMockLink } from 'utils/StaticMockLink'; -import { toast } from 'react-toastify'; - -const MOCKS = [ - { - request: { - query: USER_DETAILS, - variables: { - userId: '1', - }, - }, - result: { - data: { - user: { - __typename: 'UserData', - appUserProfile: { - __typename: 'AppUserProfile', - _id: '1', - adminFor: [ - { __typename: 'Organization', _id: '65e0df0906dd1228350cfd4a' }, - { __typename: 'Organization', _id: '65e0e2abb92c9f3e29503d4e' }, - ], - createdEvents: [ - { __typename: 'Event', _id: '65e32a5b2a1f4288ca1f086a' }, - ], - eventAdmin: [ - { __typename: 'Event', _id: '65e32a5b2a1f4288ca1f086a' }, - ], - createdOrganizations: [ - { __typename: 'Organization', _id: '65e0df0906dd1228350cfd4a' }, - { __typename: 'Organization', _id: '65e0e2abb92c9f3e29503d4e' }, - ], - pluginCreationAllowed: true, - appLanguageCode: 'fr', - isSuperAdmin: true, - adminApproved: true, - }, - user: { - __typename: 'User', - _id: '1', - firstName: 'Aditya', - lastName: 'Agarwal', - createdAt: '2024-02-26T10:36:33.098Z', - image: null, - email: 'adi79@gmail.com', - joinedOrganizations: [ - { __typename: 'Organization', _id: '65e0df0906dd1228350cfd4a' }, - { __typename: 'Organization', _id: '65e0e2abb92c9f3e29503d4e' }, - ], - membershipRequests: [], - registeredEvents: [ - { __typename: 'Event', _id: '65e32a5b2a1f4288ca1f086a' }, - ], - organizationsBlockedBy: [], - }, - }, - }, - }, - }, - { - request: { - query: UPDATE_USER_MUTATION, - variable: { - data: { - firstName: 'Adi', - lastName: 'Agarwal', - email: 'adi79@gmail.com', - appLanguageCode: 'en', - }, - file: null, - }, - }, - result: { - data: { - users: [ - { - _id: '1', - }, - ], - }, - }, - }, -]; - -const link = new StaticMockLink(MOCKS, true); - -async function wait(ms = 5): Promise { - await act(() => { - return new Promise((resolve) => { - setTimeout(resolve, ms); - }); - }); -} - -describe('Testing User Update', () => { - const props = { - key: '123', - id: '1', - toggleStateValue: jest.fn(), - }; - - const formData = { - firstName: 'Adi', - lastName: 'Agarwal', - email: 'adi79@gmail.com', - image: new File(['hello'], 'hello.png', { type: 'image/png' }), - }; - - global.alert = jest.fn(); - - test('should render props and text elements test for the page component', async () => { - render( - - - - - - - , - ); - - await wait(); - - userEvent.clear(screen.getByPlaceholderText(/First Name/i)); - - userEvent.type( - screen.getByPlaceholderText(/First Name/i), - formData.firstName, - ); - - userEvent.clear(screen.getByPlaceholderText(/Last Name/i)); - - userEvent.type( - screen.getByPlaceholderText(/Last Name/i), - formData.lastName, - ); - - userEvent.clear(screen.getByPlaceholderText(/Email/i)); - - userEvent.type(screen.getByPlaceholderText(/Email/i), formData.email); - userEvent.selectOptions(screen.getByTestId('applangcode'), 'Français'); - userEvent.upload(screen.getByLabelText(/Display Image:/i), formData.image); - await wait(); - - userEvent.click(screen.getByText(/Save Changes/i)); - - expect(screen.getByPlaceholderText(/First Name/i)).toHaveValue( - formData.firstName, - ); - expect(screen.getByPlaceholderText(/Last Name/i)).toHaveValue( - formData.lastName, - ); - expect(screen.getByPlaceholderText(/Email/i)).toHaveValue(formData.email); - - expect(screen.getByText(/Cancel/i)).toBeTruthy(); - expect(screen.getByPlaceholderText(/First Name/i)).toBeInTheDocument(); - expect(screen.getByPlaceholderText(/Last Name/i)).toBeInTheDocument(); - expect(screen.getByPlaceholderText(/Email/i)).toBeInTheDocument(); - expect(screen.getByText(/Display Image/i)).toBeInTheDocument(); - }); - test('should display warnings for blank form submission', async () => { - jest.spyOn(toast, 'warning'); - - render( - - - - - - - , - ); - - await wait(); - - userEvent.clear(screen.getByPlaceholderText(/First Name/i)); - userEvent.clear(screen.getByPlaceholderText(/Last Name/i)); - userEvent.clear(screen.getByPlaceholderText(/Email/i)); - - userEvent.click(screen.getByText(/Save Changes/i)); - - expect(toast.warning).toHaveBeenCalledWith('First Name cannot be blank!'); - expect(toast.warning).toHaveBeenCalledWith('Last Name cannot be blank!'); - expect(toast.warning).toHaveBeenCalledWith('Email cannot be blank!'); - }); -}); diff --git a/src/components/UserUpdate/UserUpdate.tsx b/src/components/UserUpdate/UserUpdate.tsx deleted file mode 100644 index d2914a99d9..0000000000 --- a/src/components/UserUpdate/UserUpdate.tsx +++ /dev/null @@ -1,269 +0,0 @@ -import React from 'react'; -import { useMutation, useQuery } from '@apollo/client'; -import { UPDATE_USER_MUTATION } from 'GraphQl/Mutations/mutations'; -import { useTranslation } from 'react-i18next'; -import Button from 'react-bootstrap/Button'; -import styles from './UserUpdate.module.css'; -import convertToBase64 from 'utils/convertToBase64'; -import { USER_DETAILS } from 'GraphQl/Queries/Queries'; -import { useLocation, useNavigate } from 'react-router-dom'; - -import { languages } from 'utils/languages'; -import { toast } from 'react-toastify'; -import { errorHandler } from 'utils/errorHandler'; -import { Form } from 'react-bootstrap'; -import Loader from 'components/Loader/Loader'; -import useLocalStorage from 'utils/useLocalstorage'; - -interface InterfaceUserUpdateProps { - id: string; - toggleStateValue: () => void; -} - -const UserUpdate: React.FC = ({ - id, - toggleStateValue, -}): JSX.Element => { - const location = useLocation(); - const navigate = useNavigate(); - const { getItem, setItem } = useLocalStorage(); - const currentUrl = location.state?.id || getItem('id') || id; - const { t } = useTranslation('translation', { - keyPrefix: 'userUpdate', - }); - const [formState, setFormState] = React.useState({ - firstName: '', - lastName: '', - email: '', - password: '', - applangcode: '', - file: '', - }); - - const [updateUser] = useMutation(UPDATE_USER_MUTATION); - - const { - data: data, - loading: loading, - error: error, - } = useQuery(USER_DETAILS, { - variables: { userId: currentUrl }, // For testing we are sending the id as a prop - }); - React.useEffect(() => { - if (data) { - setFormState({ - ...formState, - firstName: data?.user?.user?.firstName, - lastName: data?.user?.user?.lastName, - email: data?.user?.user?.email, - applangcode: data?.user?.appUserProfile?.appLanguageCode, - }); - } - }, [data]); - - if (loading) { - return ; - } - - /* istanbul ignore next */ - if (error) { - navigate(`/orgsettings/${currentUrl}`); - } - - const loginLink = async (): Promise => { - try { - const firstName = formState.firstName; - const lastName = formState.lastName; - const email = formState.email; - const file = formState.file; - const appLangCode = formState.applangcode; - let toSubmit = true; - if (firstName.trim().length == 0 || !firstName) { - toast.warning('First Name cannot be blank!'); - toSubmit = false; - } - if (lastName.trim().length == 0 || !lastName) { - toast.warning('Last Name cannot be blank!'); - toSubmit = false; - } - if (email.trim().length == 0 || !email) { - toast.warning('Email cannot be blank!'); - toSubmit = false; - } - if (!toSubmit) return; - const { data } = await updateUser({ - variables: { - //Currently on these fields are supported by the api - data: { - firstName: firstName, - lastName: lastName, - email: email, - appLanguageCode: appLangCode, - }, - file: file, - }, - }); - /* istanbul ignore next */ - if (data) { - if (getItem('id') === currentUrl) { - setItem('FirstName', firstName); - setItem('LastName', lastName); - setItem('Email', email); - setItem('UserImage', file); - } - setFormState({ - firstName: '', - lastName: '', - email: '', - password: '', - applangcode: '', - file: '', - }); - - toast.success('Successful updated'); - - navigate(0); - } - } catch (error: any) { - /* istanbul ignore next */ - errorHandler(t, error); - } - }; - - /* istanbul ignore next */ - const cancelUpdate = (): void => { - toggleStateValue(); - }; - - return ( - <> -
-
- {/*

Update Your Details

*/} -
-
- - { - setFormState({ - ...formState, - firstName: e.target.value, - }); - }} - /> -
-
-
-
- - { - setFormState({ - ...formState, - lastName: e.target.value, - }); - }} - /> -
-
-
-
- - { - setFormState({ - ...formState, - email: e.target.value, - }); - }} - /> -
-
- -
-
- -
-
-
- -
- - -
-
-
-
- - ); -}; -export default UserUpdate; diff --git a/src/screens/MemberDetail/MemberDetail.tsx b/src/screens/MemberDetail/MemberDetail.tsx index aee32e0eb6..520a965e65 100644 --- a/src/screens/MemberDetail/MemberDetail.tsx +++ b/src/screens/MemberDetail/MemberDetail.tsx @@ -149,7 +149,7 @@ const MemberDetail: React.FC = ({ id }): JSX.Element => { const loginLink = async (): Promise => { try { - console.log(formState); + // console.log(formState); const firstName = formState.firstName; const lastName = formState.lastName; const email = formState.email; From 2a5657a3d956d7cecef9149f4c3d15c3e5f29c11 Mon Sep 17 00:00:00 2001 From: Pranshu Date: Wed, 27 Mar 2024 02:25:35 +0530 Subject: [PATCH 21/21] Add user profile fields and update app user profile in UserSidebar.test.tsx --- .../UserSidebar/UserSidebar.test.tsx | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/components/UserPortal/UserSidebar/UserSidebar.test.tsx b/src/components/UserPortal/UserSidebar/UserSidebar.test.tsx index 69081b0b8f..b62eae9ed0 100644 --- a/src/components/UserPortal/UserSidebar/UserSidebar.test.tsx +++ b/src/components/UserPortal/UserSidebar/UserSidebar.test.tsx @@ -39,6 +39,20 @@ const MOCKS = [ joinedOrganizations: [], membershipRequests: [], registeredEvents: [], + gender: '', + birthDate: '2024-03-14', + educationGrade: '', + employmentStatus: '', + maritalStatus: '', + address: { + line1: '', + countryCode: '', + city: '', + state: '', + }, + phone: { + mobile: '', + }, }, appUserProfile: { _id: 'properId', @@ -48,6 +62,8 @@ const MOCKS = [ createdEvents: [], eventAdmin: [], isSuperAdmin: true, + pluginCreationAllowed: true, + appLanguageCode: 'en', }, }, }, @@ -100,6 +116,20 @@ const MOCKS = [ joinedOrganizations: [], membershipRequests: [], registeredEvents: [], + gender: '', + birthDate: '2024-03-14', + educationGrade: '', + employmentStatus: '', + maritalStatus: '', + address: { + line1: '', + countryCode: '', + city: '', + state: '', + }, + phone: { + mobile: '', + }, }, appUserProfile: { _id: '2', @@ -109,6 +139,8 @@ const MOCKS = [ eventAdmin: [], isSuperAdmin: true, adminApproved: true, + pluginCreationAllowed: true, + appLanguageCode: 'en', }, }, }, @@ -161,6 +193,20 @@ const MOCKS = [ joinedOrganizations: [], membershipRequests: [], registeredEvents: [], + gender: '', + birthDate: '2024-03-14', + educationGrade: '', + employmentStatus: '', + maritalStatus: '', + address: { + line1: '', + countryCode: '', + city: '', + state: '', + }, + phone: { + mobile: '', + }, }, appUserProfile: { _id: 'orgEmpty', @@ -170,6 +216,8 @@ const MOCKS = [ createdEvents: [], eventAdmin: [], isSuperAdmin: true, + pluginCreationAllowed: true, + appLanguageCode: 'en', }, }, },