From bd1e2c485abe25f3572db01e80b9f451de2e7820 Mon Sep 17 00:00:00 2001 From: Eric Bolikowski Date: Mon, 31 Jan 2022 16:27:47 +0100 Subject: [PATCH 01/25] docs(connect): remove obselete comment --- .../src/components/organisms/EditableMentoringTopics.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/redi-connect/src/components/organisms/EditableMentoringTopics.tsx b/apps/redi-connect/src/components/organisms/EditableMentoringTopics.tsx index ae08f56c3..fb1b45e6a 100644 --- a/apps/redi-connect/src/components/organisms/EditableMentoringTopics.tsx +++ b/apps/redi-connect/src/components/organisms/EditableMentoringTopics.tsx @@ -121,9 +121,6 @@ const CategoryGroup = ({ onChange, formik, }: any) => { - // The current REDI_LOCATION might not use the current CategoryGroup (e.g. - // Munich doesnt, at the time or writing, use 'coding' or 'other'. If it's the case, return null - if (!categoriesByGroup[id]) return null return ( From 3cd789d90d97c5fdb7bed9b08108e58577dcdfcb Mon Sep 17 00:00:00 2001 From: Eric Bolikowski Date: Mon, 31 Jan 2022 16:53:15 +0100 Subject: [PATCH 02/25] feat(con): create new data lists --- libs/shared-config/src/lib/mentoring-data-lists.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 libs/shared-config/src/lib/mentoring-data-lists.ts diff --git a/libs/shared-config/src/lib/mentoring-data-lists.ts b/libs/shared-config/src/lib/mentoring-data-lists.ts new file mode 100644 index 000000000..a1484effa --- /dev/null +++ b/libs/shared-config/src/lib/mentoring-data-lists.ts @@ -0,0 +1,12 @@ +export const MENTORING_GOALS = { + abc: 'ABC', + abc_plus: 'ABC+', + abc_plus_plus: 'ABC++', + abc_plus_plus_plus: 'ABC+++', +} as const + +export const PROFESSIONAL_EXPERIENCE_FIELDS = { + none: 'None', + some: 'Some', + professional: 'Professional', +} as const From 11c6b6e4eabe63513d9b6f0f8e4644232c68ac9f Mon Sep 17 00:00:00 2001 From: Eric Bolikowski Date: Mon, 7 Mar 2022 20:19:39 +0100 Subject: [PATCH 03/25] feat(connect/mentor profile): refactored mentoring topics --- .../molecules/ReadMentoringGoals.tsx | 56 ++ .../molecules/ReadMentoringTopicsNew2022.tsx | 55 ++ .../ReadProfessionalExperienceFields.tsx | 55 ++ .../organisms/EditableMentoringGoals.tsx | 134 +++++ .../EditableMentoringTopicsNew2022.tsx | 152 ++++++ .../EditableProfessionalExperienceFields.tsx | 138 +++++ apps/redi-connect/src/pages/app/me/Me.tsx | 22 +- libs/shared-config/src/index.ts | 1 + libs/shared-config/src/lib/config.ts | 146 ----- .../src/lib/mentoring-data-lists.ts | 499 +++++++++++++++++- libs/shared-types/src/lib/RedProfile.ts | 6 + 11 files changed, 1107 insertions(+), 157 deletions(-) create mode 100644 apps/redi-connect/src/components/molecules/ReadMentoringGoals.tsx create mode 100644 apps/redi-connect/src/components/molecules/ReadMentoringTopicsNew2022.tsx create mode 100644 apps/redi-connect/src/components/molecules/ReadProfessionalExperienceFields.tsx create mode 100644 apps/redi-connect/src/components/organisms/EditableMentoringGoals.tsx create mode 100644 apps/redi-connect/src/components/organisms/EditableMentoringTopicsNew2022.tsx create mode 100644 apps/redi-connect/src/components/organisms/EditableProfessionalExperienceFields.tsx diff --git a/apps/redi-connect/src/components/molecules/ReadMentoringGoals.tsx b/apps/redi-connect/src/components/molecules/ReadMentoringGoals.tsx new file mode 100644 index 000000000..c3990d037 --- /dev/null +++ b/apps/redi-connect/src/components/molecules/ReadMentoringGoals.tsx @@ -0,0 +1,56 @@ +import { + Caption, + CardTags, + CardTagsProps, + Placeholder, +} from '@talent-connect/shared-atomic-design-components' +import { MENTORING_GOALS } from '@talent-connect/shared-config' +import { RedProfile } from '@talent-connect/shared-types' +import React from 'react' +import { connect } from 'react-redux' +import { RootState } from '../../redux/types' + +interface ReadMentoringProps { + profile: RedProfile + caption?: boolean +} + +export const ProfileTags = ({ items, shortList }: CardTagsProps) => ( + MENTORING_GOALS[item]} + /> +) + +const ReadMentoringGoals = ({ profile, caption }: ReadMentoringProps) => { + const { mentoringGoals } = profile + + if (!mentoringGoals?.length && !caption) + return ( + + Select at least one goal you would like to support mentees with + + ) + + return ( + <> + {caption && Mentoring goals} + + + ) +} + +const mapStateToProps = (state: RootState) => ({ + profile: state.user.profile as RedProfile, +}) + +export default { + Me: connect(mapStateToProps, {})(ReadMentoringGoals), + Some: ({ profile }: ReadMentoringProps) => ( + + ), + Tags: ({ items, shortList }: CardTagsProps) => ( + + ), +} diff --git a/apps/redi-connect/src/components/molecules/ReadMentoringTopicsNew2022.tsx b/apps/redi-connect/src/components/molecules/ReadMentoringTopicsNew2022.tsx new file mode 100644 index 000000000..48f62fadd --- /dev/null +++ b/apps/redi-connect/src/components/molecules/ReadMentoringTopicsNew2022.tsx @@ -0,0 +1,55 @@ +import { + Caption, + CardTags, + CardTagsProps, + Placeholder, +} from '@talent-connect/shared-atomic-design-components' +import { MENTORING_TOPICS_MAP } from '@talent-connect/shared-config' +import { RedProfile } from '@talent-connect/shared-types' +import React from 'react' +import { connect } from 'react-redux' +import { RootState } from '../../redux/types' + +interface ReadMentoringProps { + profile: RedProfile + caption?: boolean +} + +export const ProfileTags = ({ items, shortList }: CardTagsProps) => ( + MENTORING_TOPICS_MAP[item]} + /> +) + +const ReadMentoringTopicsNew2022 = ({ + profile, + caption, +}: ReadMentoringProps) => { + const { mentoringTopics } = profile + + if (!mentoringTopics?.length && !caption) + return Please pick mentoring topics. + + return ( + <> + {caption && {'Mentoring Topics'}} + + + ) +} + +const mapStateToProps = (state: RootState) => ({ + profile: state.user.profile as RedProfile, +}) + +export default { + Me: connect(mapStateToProps, {})(ReadMentoringTopicsNew2022), + Some: ({ profile }: ReadMentoringProps) => ( + + ), + Tags: ({ items, shortList }: CardTagsProps) => ( + + ), +} diff --git a/apps/redi-connect/src/components/molecules/ReadProfessionalExperienceFields.tsx b/apps/redi-connect/src/components/molecules/ReadProfessionalExperienceFields.tsx new file mode 100644 index 000000000..cb0310a3c --- /dev/null +++ b/apps/redi-connect/src/components/molecules/ReadProfessionalExperienceFields.tsx @@ -0,0 +1,55 @@ +import { + Caption, + CardTags, + CardTagsProps, + Placeholder, +} from '@talent-connect/shared-atomic-design-components' +import { PROFESSIONAL_EXPERIENCE_FIELDS } from '@talent-connect/shared-config' +import { RedProfile } from '@talent-connect/shared-types' +import React from 'react' +import { connect } from 'react-redux' +import { RootState } from '../../redux/types' + +interface ReadMentoringProps { + profile: RedProfile + caption?: boolean +} + +export const ProfileTags = ({ items, shortList }: CardTagsProps) => ( + PROFESSIONAL_EXPERIENCE_FIELDS[item]} + /> +) + +const ReadProfessionalExperienceFields = ({ + profile, + caption, +}: ReadMentoringProps) => { + const { professionalExperienceFields } = profile + + if (!professionalExperienceFields?.length && !caption) + return Select your fields of expertise + + return ( + <> + {caption && Professional experience} + + + ) +} + +const mapStateToProps = (state: RootState) => ({ + profile: state.user.profile as RedProfile, +}) + +export default { + Me: connect(mapStateToProps, {})(ReadProfessionalExperienceFields), + Some: ({ profile }: ReadMentoringProps) => ( + + ), + Tags: ({ items, shortList }: CardTagsProps) => ( + + ), +} diff --git a/apps/redi-connect/src/components/organisms/EditableMentoringGoals.tsx b/apps/redi-connect/src/components/organisms/EditableMentoringGoals.tsx new file mode 100644 index 000000000..1d8901c6e --- /dev/null +++ b/apps/redi-connect/src/components/organisms/EditableMentoringGoals.tsx @@ -0,0 +1,134 @@ +import { + Checkbox, + Editable, +} from '@talent-connect/shared-atomic-design-components' +import { MENTORING_GOALS } from '@talent-connect/shared-config' +import { RedProfile } from '@talent-connect/shared-types' +import { objectEntries } from '@talent-connect/typescript-utilities' +import { FormikValues, useFormik } from 'formik' +import React from 'react' +import { Content, Element } from 'react-bulma-components' +import { connect } from 'react-redux' +import * as Yup from 'yup' +import { RootState } from '../../redux/types' +import { profileSaveStart } from '../../redux/user/actions' +import ReadMentoringGoals from '../molecules/ReadMentoringGoals' + +export interface FormValues { + isMentor: boolean + mentoringGoals: string[] +} + +const formMentoringGoals = objectEntries(MENTORING_GOALS) + +interface Props { + profile: RedProfile | undefined + profileSaveStart: Function +} + +const validationSchema = Yup.object({ + mentoringGoals: Yup.array().min(1), +}) + +const EditableMentoringGoals = ({ profile, profileSaveStart }: Props) => { + const { id, userType, mentoringGoals } = profile as RedProfile + + const submitForm = async (values: FormikValues) => { + const profileMentoring = values as Partial + profileSaveStart({ ...profileMentoring, id }) + } + + const isMentor = + userType === 'mentor' || userType === 'public-sign-up-mentor-pending-review' + + const initialValues: FormValues = { + isMentor, + mentoringGoals: mentoringGoals || [], + } + + const formik = useFormik({ + initialValues, + enableReinitialize: true, + validationSchema, + onSubmit: submitForm, + }) + + const { mentoringGoals: selectedMentoringGoals } = formik.values + + const mentoringGoalsChange = (e: any) => { + e.persist() + const value = e.target.value + let newMentoringGoals + if (e.target.checked) { + newMentoringGoals = selectedMentoringGoals.concat(value) + } else { + newMentoringGoals = selectedMentoringGoals.filter( + (cat: any) => cat !== value + ) + } + formik.setFieldValue('mentoringGoals', newMentoringGoals) + formik.setFieldTouched('mentoringGoals', true, false) + } + + return ( + formik.handleSubmit()} + onClose={() => formik.resetForm()} + savePossible={formik.dirty && formik.isValid} + read={} + className="mentoring" + > + + Select at least one goal you would like to support mentees with + + + {formMentoringGoals.map(([fieldId, fieldLabel]) => ( + + ))} + + + ) +} + +const MentoringGoal = ({ + id, + label, + selectedMentoringGoals, + onChange, + formik, +}: any) => { + return ( + + {label} + + ) +} + +const mapStateToProps = (state: RootState) => ({ + profile: state.user.profile, +}) + +const mapDispatchToProps = (dispatch: any) => ({ + profileSaveStart: (profile: Partial) => + dispatch(profileSaveStart(profile)), +}) + +export default connect( + mapStateToProps, + mapDispatchToProps +)(EditableMentoringGoals) diff --git a/apps/redi-connect/src/components/organisms/EditableMentoringTopicsNew2022.tsx b/apps/redi-connect/src/components/organisms/EditableMentoringTopicsNew2022.tsx new file mode 100644 index 000000000..6f50ad1c1 --- /dev/null +++ b/apps/redi-connect/src/components/organisms/EditableMentoringTopicsNew2022.tsx @@ -0,0 +1,152 @@ +import { + Checkbox, + Editable, +} from '@talent-connect/shared-atomic-design-components' +import { + MentoringTopicKey, + MENTORING_TOPICS, + MENTORING_TOPIC_GROUPS, +} from '@talent-connect/shared-config' +import { RedProfile } from '@talent-connect/shared-types' +import { objectEntries } from '@talent-connect/typescript-utilities' +import { FormikValues, useFormik } from 'formik' +import groupBy from 'lodash/groupBy' +import React from 'react' +import { Columns, Content, Element, Heading } from 'react-bulma-components' +import { connect } from 'react-redux' +import { RootState } from '../../redux/types' +import { profileSaveStart } from '../../redux/user/actions' +import ReadMentoringTopicsNew2022 from '../molecules/ReadMentoringTopicsNew2022' + +export type UserType = + | 'mentor' + | 'mentee' + | 'public-sign-up-mentor-pending-review' + | 'public-sign-up-mentee-pending-review' + +export interface FormValues { + mentoringTopics: MentoringTopicKey[] +} + +const mentoringTopicsByGroup = groupBy(MENTORING_TOPICS, (topic) => topic.group) + +const formMentoringTopicGroups = objectEntries(MENTORING_TOPIC_GROUPS) + +interface Props { + profile: RedProfile | undefined + profileSaveStart: Function +} + +const EditableMentoringTopics = ({ profile, profileSaveStart }: Props) => { + const { id, userType, mentoringTopics } = profile as RedProfile + + const submitForm = async (values: FormikValues) => { + const profileMentoring = values as Partial + profileSaveStart({ ...profileMentoring, id }) + } + + const initialValues: FormValues = { + mentoringTopics: mentoringTopics || [], + } + + const formik = useFormik({ + initialValues, + enableReinitialize: true, + onSubmit: submitForm, + }) + + const { mentoringTopics: selectedMentoringTopics } = formik.values + + const mentoringTopicsChange = (e: any) => { + e.persist() + const value = e.target.value + let newMentoringTopics + if (e.target.checked) { + newMentoringTopics = selectedMentoringTopics.concat(value) + } else { + newMentoringTopics = selectedMentoringTopics.filter( + (cat: any) => cat !== value + ) + } + formik.setFieldValue('mentoringTopics', newMentoringTopics) + formik.setFieldTouched('mentoringTopics', true, false) + } + + return ( + formik.handleSubmit()} + onClose={() => formik.resetForm()} + savePossible={formik.dirty && formik.isValid} + read={} + className="mentoring" + > + + Select at least one topic where you would like to support mentees. + + + {formMentoringTopicGroups.map(([groupId, groupLabel]) => ( + + ))} + + + ) +} + +const MentoringTopicGroup = ({ + id, + label, + selectedMentoringTopics, + onChange, + formik, +}: any) => { + if (!mentoringTopicsByGroup[id]) return null + return ( + + + {label} + + + {mentoringTopicsByGroup[id].map((groupItem) => ( + + {groupItem.label} + + ))} + + + ) +} + +const mapStateToProps = (state: RootState) => ({ + profile: state.user.profile, +}) + +const mapDispatchToProps = (dispatch: any) => ({ + profileSaveStart: (profile: Partial) => + dispatch(profileSaveStart(profile)), +}) + +export default connect( + mapStateToProps, + mapDispatchToProps +)(EditableMentoringTopics) diff --git a/apps/redi-connect/src/components/organisms/EditableProfessionalExperienceFields.tsx b/apps/redi-connect/src/components/organisms/EditableProfessionalExperienceFields.tsx new file mode 100644 index 000000000..dbe134250 --- /dev/null +++ b/apps/redi-connect/src/components/organisms/EditableProfessionalExperienceFields.tsx @@ -0,0 +1,138 @@ +import { + Checkbox, + Editable, +} from '@talent-connect/shared-atomic-design-components' +import { PROFESSIONAL_EXPERIENCE_FIELDS } from '@talent-connect/shared-config' +import { RedProfile } from '@talent-connect/shared-types' +import { objectEntries } from '@talent-connect/typescript-utilities' +import { FormikValues, useFormik } from 'formik' +import React from 'react' +import { Content, Element } from 'react-bulma-components' +import { connect } from 'react-redux' +import { RootState } from '../../redux/types' +import { profileSaveStart } from '../../redux/user/actions' +import { ReadMentoringTopics } from '../molecules' +import ReadProfessionalExperienceFields from '../molecules/ReadProfessionalExperienceFields' + +export interface FormValues { + isMentor: boolean + professionalExperienceFields: string[] +} + +const formProfessionalExperienceFields = objectEntries( + PROFESSIONAL_EXPERIENCE_FIELDS +) + +interface Props { + profile: RedProfile | undefined + profileSaveStart: Function +} + +const EditableProfessionalExperienceFields = ({ + profile, + profileSaveStart, +}: Props) => { + const { id, userType, professionalExperienceFields } = profile as RedProfile + + const submitForm = async (values: FormikValues) => { + const profileMentoring = values as Partial + profileSaveStart({ ...profileMentoring, id }) + } + + const isMentor = + userType === 'mentor' || userType === 'public-sign-up-mentor-pending-review' + + const initialValues: FormValues = { + isMentor, + professionalExperienceFields: professionalExperienceFields || [], + } + + const formik = useFormik({ + initialValues, + enableReinitialize: true, + onSubmit: submitForm, + }) + + const { professionalExperienceFields: selectedProfessionalExperienceFields } = + formik.values + + const professionalExperienceFieldsChange = (e: any) => { + e.persist() + const value = e.target.value + let newProfessionalExperienceFields + if (e.target.checked) { + newProfessionalExperienceFields = + selectedProfessionalExperienceFields.concat(value) + } else { + newProfessionalExperienceFields = + selectedProfessionalExperienceFields.filter((cat: any) => cat !== value) + } + formik.setFieldValue( + 'professionalExperienceFields', + newProfessionalExperienceFields + ) + formik.setFieldTouched('professionalExperienceFields', true, false) + } + + return ( + formik.handleSubmit()} + onClose={() => formik.resetForm()} + savePossible={formik.dirty && formik.isValid} + read={} + className="mentoring" + > + Select your fields of expertise + + {formProfessionalExperienceFields.map(([fieldId, fieldLabel]) => ( + + ))} + + + ) +} + +const ProfessionalExperienceField = ({ + id, + label, + selectedProfessionalExperienceFields, + onChange, + formik, +}: any) => { + return ( + + {label} + + ) +} + +const mapStateToProps = (state: RootState) => ({ + profile: state.user.profile, +}) + +const mapDispatchToProps = (dispatch: any) => ({ + profileSaveStart: (profile: Partial) => + dispatch(profileSaveStart(profile)), +}) + +export default connect( + mapStateToProps, + mapDispatchToProps +)(EditableProfessionalExperienceFields) diff --git a/apps/redi-connect/src/pages/app/me/Me.tsx b/apps/redi-connect/src/pages/app/me/Me.tsx index 381370e07..528426123 100644 --- a/apps/redi-connect/src/pages/app/me/Me.tsx +++ b/apps/redi-connect/src/pages/app/me/Me.tsx @@ -22,6 +22,9 @@ import { } from '../../../components/organisms' import { LoggedIn } from '../../../components/templates' +import EditableMentoringTopicsNew2022 from '../../../components/organisms/EditableMentoringTopicsNew2022' +import EditableProfessionalExperienceFields from '../../../components/organisms/EditableProfessionalExperienceFields' +import EditableMentoringGoals from '../../../components/organisms/EditableMentoringGoals' // CHECK OUT THE LOADER const Me = ({ loading, saveResult, profileFetchStart, profile }: any) => { @@ -75,10 +78,6 @@ const Me = ({ loading, saveResult, profileFetchStart, profile }: any) => { - - - - {userIsMentor && ( @@ -137,6 +136,21 @@ const Me = ({ loading, saveResult, profileFetchStart, profile }: any) => { + + + + + + + + + + + + + + + ) } diff --git a/libs/shared-config/src/index.ts b/libs/shared-config/src/index.ts index 92aae0c0a..814336830 100644 --- a/libs/shared-config/src/index.ts +++ b/libs/shared-config/src/index.ts @@ -1 +1,2 @@ export * from './lib/config' +export * from './lib/mentoring-data-lists' diff --git a/libs/shared-config/src/lib/config.ts b/libs/shared-config/src/lib/config.ts index 9c6535d57..74a2bf0b4 100644 --- a/libs/shared-config/src/lib/config.ts +++ b/libs/shared-config/src/lib/config.ts @@ -4,152 +4,6 @@ export const REDI_LOCATION_NAMES = { nrw: 'NRW', } as const -export const CATEGORY_GROUPS = { - softwareEngineering: '๐Ÿ‘ฉโ€๐Ÿ’ป Software Engineering', - design: '๐ŸŽจ Design', - otherProfessions: '๐Ÿ„โ€โ™€๏ธ Other Professions', - careerSupport: 'โœ‹ Career Support', - language: '๐Ÿ—ฃ๏ธ Language Support', - other: '๐Ÿค— Other', -} as const - -export const CATEGORIES = [ - { - id: 'basicProgrammingSkills', - label: 'Basic programming skills', - group: 'softwareEngineering', - }, - { id: 'htmlCss', label: 'HTML & CSS', group: 'softwareEngineering' }, - { id: 'javascript', label: 'Javascript', group: 'softwareEngineering' }, - { id: 'react', label: 'React', group: 'softwareEngineering' }, - { id: 'java', label: 'Java', group: 'softwareEngineering' }, - { id: 'python', label: 'Python', group: 'softwareEngineering' }, - { - id: 'dataAnalytics', - label: 'Data Analytics', - group: 'softwareEngineering', - }, - { - id: 'machineLearning', - label: 'Machine Learning', - group: 'softwareEngineering', - }, - { - id: 'mobileDevelopmentIos', - label: 'iOS Mobile Development', - group: 'softwareEngineering', - }, - { - id: 'mobileDevelopmentAndroid', - label: 'Android Mobile Development', - group: 'softwareEngineering', - }, - { id: 'salesforce', label: 'Salesforce', group: 'softwareEngineering' }, - { - id: 'devOpsCloud', - label: 'DevOps and Cloud (e.g. Azure, AWS)', - group: 'softwareEngineering', - }, - { id: 'iot', label: 'IoT', group: 'softwareEngineering' }, - { - id: 'computerNetworking', - label: 'Computer Networking', - group: 'softwareEngineering', - }, - { id: 'blockchain', label: 'Blockchain', group: 'softwareEngineering' }, - { - id: 'productManagement', - label: 'Product Management', - group: 'otherProfessions', - }, - { - id: 'projectManagement', - label: 'Project Management', - group: 'otherProfessions', - }, - { - id: 'digitalMarketing', - label: 'Digital Marketing', - group: 'otherProfessions', - }, - { - id: 'businessDevelopment', - label: 'Business Development', - group: 'otherProfessions', - }, - { id: 'sales', label: 'Sales', group: 'otherProfessions' }, - { - id: 'qualityAssurance', - label: 'Quality Assurance', - group: 'otherProfessions', - }, - { id: 'basicGerman', label: 'Basic German ๐Ÿ‡ฉ๐Ÿ‡ช', group: 'language' }, - { id: 'businessGerman', label: 'Business German ๐Ÿ‡ฉ๐Ÿ‡ช', group: 'language' }, - { id: 'english', label: 'English ๐Ÿ‡ฌ๐Ÿ‡ง', group: 'language' }, - { id: 'graphicDesign', label: 'Graphic Design', group: 'design' }, - { - id: 'userInterfaceDesign', - label: 'User Interface Design', - group: 'design', - }, - { - id: 'userExperienceDesign', - label: 'User Experience Design', - group: 'design', - }, - { - id: 'motivationAndEncouragement', - label: 'Motivation & encouragement', - group: 'other', - }, - { id: 'friendAndHelp', label: 'Be a friend and help', group: 'other' }, - { id: 'dontKnowYet', label: "I don't know yet", group: 'other' }, - { - id: 'careerOrientationAndPlanning', - label: 'Career orientation & planning', - group: 'careerSupport', - }, - { - id: 'internshipOrWorkingStudent', - label: 'Internship / working student position search', - group: 'careerSupport', - }, - { id: 'jobSearch', label: 'Job search', group: 'careerSupport' }, - { - id: 'jobApplicationsCvPreparationEnglish', - label: 'Job applications and CV preparation in English', - group: 'careerSupport', - }, - { - id: 'jobApplicationsCvPreparationGerman', - label: 'Job applications and CV preparation in German', - group: 'careerSupport', - }, - { - id: 'interviewPreparation', - label: 'Interview preparation', - group: 'careerSupport', - }, - { - id: 'codingChallengePreparation', - label: 'Coding challenge preparation', - group: 'careerSupport', - }, - { - id: 'buildingProfessionalNetwork', - label: 'Building a professional network', - group: 'careerSupport', - }, - { id: 'entrepreneurship', label: 'Entrepreneurship', group: 'careerSupport' }, - { id: 'freelancing', label: 'Freelancing', group: 'careerSupport' }, -] as const -export type CategoryKey = typeof CATEGORIES[number]['id'] -export type CategoryLabel = typeof CATEGORIES[number]['label'] - -export const CATEGORIES_MAP = Object.fromEntries( - CATEGORIES.map((cat) => [cat.id, cat.label]) -) as Record - export const LANGUAGES = [ 'Afrikaans', 'Albanian', diff --git a/libs/shared-config/src/lib/mentoring-data-lists.ts b/libs/shared-config/src/lib/mentoring-data-lists.ts index a1484effa..f40cf4531 100644 --- a/libs/shared-config/src/lib/mentoring-data-lists.ts +++ b/libs/shared-config/src/lib/mentoring-data-lists.ts @@ -1,12 +1,497 @@ export const MENTORING_GOALS = { - abc: 'ABC', - abc_plus: 'ABC+', - abc_plus_plus: 'ABC++', - abc_plus_plus_plus: 'ABC+++', + buildingAProfessionalNetwork: 'Building a professional network', + jobSearchAndApplicationProcess: 'Job search and application process', + entrepreneurshipAndFreelancing: 'Entrepreneurship and freelancing', + tutoringInAParticularSkillTool: 'Tutoring in a particular skill / tool', + careerOrientatioPlanning: 'Career orientation & planning', + preparationForACertificationInterview: + 'Preparation for a certification / interview', } as const +export type MentoringGoalKey = keyof typeof MENTORING_GOALS + export const PROFESSIONAL_EXPERIENCE_FIELDS = { - none: 'None', - some: 'Some', - professional: 'Professional', + humanResourcesAndRecruiting: 'Human resources and recruiting', + marketingSocialMediaAndSales: 'Marketing, social media and sales', + projectAndProductManagement: 'Project and product management', + requirementsAndBusinessAnalysis: 'Requirements and business analysis', + softwareDevelopment: 'Software development', + testingAndQa: 'Testing and QA', + usabilityAndUiDesign: 'Usability and UI design', +} as const + +export type ProfessionalExperienceFieldKey = + keyof typeof PROFESSIONAL_EXPERIENCE_FIELDS + +export const MENTORING_TOPIC_GROUPS = { + overarchingTopics: '๐ŸŒˆ Overarching topics', + productAndProjectManagement: '๐Ÿ“‹ Product and project management', + softwareDevelopment: '๐Ÿ‘ฉโ€๐Ÿ’ป Software development', + requirementsAnalysisAndResearch: '๐Ÿงช Requirements analysis and research', + uxAndUiDesign: '๐ŸŽจ UX and UI design', + testingAndQualityAssurance: '๐ŸŽ๏ธ Testing and Quality Assurance', + toolsAndFrameworks: '๐Ÿ› ๏ธ Tools and frameworks', +} as const + +export const MENTORING_TOPICS = [ + { + id: 'applicationProcessAndPortfolio', + label: 'Application process and portfolio', + group: 'overarchingTopics', + }, + { id: 'communication', label: 'Communication', group: 'overarchingTopics' }, + { + id: 'crossCulturalTeams', + label: 'Cross-cultural teams', + group: 'overarchingTopics', + }, + { + id: 'crossFunctionalWork', + label: 'Cross-functional work', + group: 'overarchingTopics', + }, + { id: 'facilitation', label: 'Facilitation', group: 'overarchingTopics' }, + { + id: 'givingReceivingFeedback', + label: 'Giving / receiving feedback', + group: 'overarchingTopics', + }, + { + id: 'marketingAndSocialMedia', + label: 'Marketing and social media', + group: 'overarchingTopics', + }, + { id: 'sales', label: 'Sales', group: 'overarchingTopics' }, + { + id: 'selfOrganisation', + label: 'Self-organisation', + group: 'overarchingTopics', + }, + { + id: 'socialNetworkProfileTuning', + label: 'Social network profile tuning', + group: 'overarchingTopics', + }, + { + id: 'storytellingAndPresentation', + label: 'Storytelling and presentation', + group: 'overarchingTopics', + }, + { + id: 'teamLeadership', + label: 'Team leadership', + group: 'overarchingTopics', + }, + { + id: 'agileFrameworks', + label: 'Agile frameworks', + group: 'productAndProjectManagement', + }, + { + id: 'budgetCalculation', + label: 'Budget calculation', + group: 'productAndProjectManagement', + }, + { + id: 'businessDevelopment', + label: 'Business development', + group: 'productAndProjectManagement', + }, + { + id: 'changeManagement', + label: 'Change management', + group: 'productAndProjectManagement', + }, + { + id: 'enterpriseArchitectureManagement', + label: 'Enterprise architecture management', + group: 'productAndProjectManagement', + }, + { + id: 'marketResearch', + label: 'Market research', + group: 'productAndProjectManagement', + }, + { + id: 'planningAndRoadmaps', + label: 'Planning and roadmaps', + group: 'productAndProjectManagement', + }, + { + id: 'prioritisationMetrics', + label: 'Prioritisation metrics', + group: 'productAndProjectManagement', + }, + { + id: 'productBacklogManagement', + label: 'Product Backlog management', + group: 'productAndProjectManagement', + }, + { + id: 'productManagement', + label: 'Product management', + group: 'productAndProjectManagement', + }, + { + id: 'projectManagement', + label: 'Project management', + group: 'productAndProjectManagement', + }, + { + id: 'androidMobileDevelopment', + label: 'Android Mobile Development', + group: 'softwareDevelopment', + }, + { + id: 'basicProgrammingSkills', + label: 'Basic programming skills', + group: 'softwareDevelopment', + }, + { + id: 'computerNetworking', + label: 'Computer networking', + group: 'softwareDevelopment', + }, + { + id: 'crossBrowserDevelopment', + label: 'Cross-browser development', + group: 'softwareDevelopment', + }, + { + id: 'dataAnalytics', + label: 'Data analytics', + group: 'softwareDevelopment', + }, + { + id: 'databaseManagement', + label: 'Database management', + group: 'softwareDevelopment', + }, + { + id: 'devopsAndCloud', + label: 'DevOps and Cloud', + group: 'softwareDevelopment', + }, + { + id: 'hybridAppDevelopment', + label: 'Hybrid App Development', + group: 'softwareDevelopment', + }, + { + id: 'hardwareAndNetworks', + label: 'Hardware and networks', + group: 'softwareDevelopment', + }, + { id: 'iot', label: 'IoT', group: 'softwareDevelopment' }, + { + id: 'iosMobileDevelopment', + label: 'iOS Mobile Development', + group: 'softwareDevelopment', + }, + { + id: 'machineLearning', + label: 'Machine learning', + group: 'softwareDevelopment', + }, + + { + id: 'dataAnalysis', + label: 'Data analysis', + group: 'requirementsAndBusinessAnalysis', + }, + { + id: 'designResearch', + label: 'Design research', + group: 'requirementsAndBusinessAnalysis', + }, + { + id: 'designThinking', + label: 'Design Thinking', + group: 'requirementsAndBusinessAnalysis', + }, + { + id: 'domainDrivenDesign', + label: 'Domain driven design', + group: 'requirementsAndBusinessAnalysis', + }, + { + id: 'mappingCustomerExperience', + label: 'Mapping customer experience', + group: 'requirementsAndBusinessAnalysis', + }, + { + id: 'prioritisationMetrics', + label: 'Prioritisation metrics', + group: 'requirementsAndBusinessAnalysis', + }, + { + id: 'processModelling', + label: 'Process modelling', + group: 'requirementsAndBusinessAnalysis', + }, + { + id: 'softwareDevelopmentLifeCycle', + label: 'Software Development Life Cycle', + group: 'requirementsAndBusinessAnalysis', + }, + { + id: 'technicalWriting', + label: 'Technical writing', + group: 'requirementsAndBusinessAnalysis', + }, + { id: 'animation', label: 'Animation', group: 'uxAndUiDesign' }, + { id: 'branding', label: 'Branding', group: 'uxAndUiDesign' }, + { + id: 'generalUserExperience', + label: 'General user experience', + group: 'uxAndUiDesign', + }, + { id: 'graphicDesign', label: 'Graphic Design', group: 'uxAndUiDesign' }, + { + id: 'informationArchitecture', + label: 'Information architecture', + group: 'uxAndUiDesign', + }, + { + id: 'interactionDesign', + label: 'Interaction design', + group: 'uxAndUiDesign', + }, + { + id: 'prototypingAndWireframing', + label: 'Prototyping and wireframing', + group: 'uxAndUiDesign', + }, + { + id: 'responsiveDesign', + label: 'Responsive design', + group: 'uxAndUiDesign', + }, + { + id: 'typographyColorTheory', + label: 'Typography / color theory', + group: 'uxAndUiDesign', + }, + { + id: 'automatedSoftwareTesting', + label: 'Automated software testing', + group: 'testingAndQa', + }, + { + id: 'behaviorDrivenDevelopment', + label: 'Behavior-driven development', + group: 'testingAndQa', + }, + { + id: 'crossBrowserTesting', + label: 'Cross-browser testing', + group: 'testingAndQa', + }, + { + id: 'functionalTesting', + label: 'Functional Testing', + group: 'testingAndQa', + }, + { + id: 'softwareTestingMethodologies', + label: 'Software testing methodologies', + group: 'testingAndQa', + }, + { + id: 'testDrivenDevelopment', + label: 'Test-driven development', + group: 'testingAndQa', + }, + { id: 'testPlanning', label: 'Test planning', group: 'testingAndQa' }, + { id: 'testManagement', label: 'Test management', group: 'testingAndQa' }, + { id: 'usabilityTesting', label: 'Usability testing', group: 'testingAndQa' }, + { id: 'atlassianJira', label: 'Atlassian Jira', group: 'toolsAndFrameworks' }, + { id: 'aws', label: 'AWS', group: 'toolsAndFrameworks' }, + { id: 'azure', label: 'Azure', group: 'toolsAndFrameworks' }, + { + id: 'balsamiqMockup', + label: 'Balsamiq Mockup', + group: 'toolsAndFrameworks', + }, + { id: 'blockchain', label: 'Blockchain', group: 'toolsAndFrameworks' }, + { id: 'cucumber', label: 'Cucumber', group: 'toolsAndFrameworks' }, + { id: 'cypress', label: 'Cypress', group: 'toolsAndFrameworks' }, + { id: 'docker', label: 'Docker', group: 'toolsAndFrameworks' }, + { id: 'figma', label: 'Figma', group: 'toolsAndFrameworks' }, + { id: 'flutter', label: 'Flutter', group: 'toolsAndFrameworks' }, + { id: 'gcp', label: 'GCP', group: 'toolsAndFrameworks' }, + { id: 'git', label: 'Git', group: 'toolsAndFrameworks' }, + { id: 'gherkin', label: 'Gherkin', group: 'toolsAndFrameworks' }, + { id: 'htmlCss', label: 'HTML & CSS', group: 'toolsAndFrameworks' }, + { id: 'java', label: 'Java', group: 'toolsAndFrameworks' }, + { id: 'javascript', label: 'JavaScript', group: 'toolsAndFrameworks' }, + { id: 'jest', label: 'Jest', group: 'toolsAndFrameworks' }, + { id: 'mongodb', label: 'MongoDB', group: 'toolsAndFrameworks' }, + { id: 'mysql', label: 'MySQL', group: 'toolsAndFrameworks' }, + { id: 'nodejs', label: 'NodeJS', group: 'toolsAndFrameworks' }, + { id: 'python', label: 'Python', group: 'toolsAndFrameworks' }, + { id: 'react', label: 'React', group: 'toolsAndFrameworks' }, + { id: 'reactNative', label: 'React Native', group: 'toolsAndFrameworks' }, + { id: 'restApis', label: "REST API's", group: 'toolsAndFrameworks' }, + { id: 'salesforce', label: 'Salesforce', group: 'toolsAndFrameworks' }, + { id: 'selenium', label: 'Selenium', group: 'toolsAndFrameworks' }, + { id: 'sketch', label: 'Sketch', group: 'toolsAndFrameworks' }, + { id: 'sql', label: 'SQL', group: 'toolsAndFrameworks' }, + { id: 'vmware', label: 'VMWare', group: 'toolsAndFrameworks' }, + { id: 'virtualBox', label: 'Virtual Box', group: 'toolsAndFrameworks' }, + { id: 'xray', label: 'Xray', group: 'toolsAndFrameworks' }, + { id: 'zephyr', label: 'Zephyr', group: 'toolsAndFrameworks' }, +] + +export type MentoringTopicKey = typeof CATEGORIES[number]['id'] +export type MentoringTopicLabel = typeof CATEGORIES[number]['label'] + +export const MENTORING_TOPICS_MAP = Object.fromEntries( + MENTORING_TOPICS.map((topic) => [topic.id, topic.label]) +) as Record + +export const CATEGORY_GROUPS = { + softwareEngineering: '๐Ÿ‘ฉโ€๐Ÿ’ป Software Engineering', + design: '๐ŸŽจ Design', + otherProfessions: '๐Ÿ„โ€โ™€๏ธ Other Professions', + careerSupport: 'โœ‹ Career Support', + language: '๐Ÿ—ฃ๏ธ Language Support', + other: '๐Ÿค— Other', } as const + +export const CATEGORIES = [ + { + id: 'basicProgrammingSkills', + label: 'Basic programming skills', + group: 'softwareEngineering', + }, + { id: 'htmlCss', label: 'HTML & CSS', group: 'softwareEngineering' }, + { id: 'javascript', label: 'Javascript', group: 'softwareEngineering' }, + { id: 'react', label: 'React', group: 'softwareEngineering' }, + { id: 'java', label: 'Java', group: 'softwareEngineering' }, + { id: 'python', label: 'Python', group: 'softwareEngineering' }, + { + id: 'dataAnalytics', + label: 'Data Analytics', + group: 'softwareEngineering', + }, + { + id: 'machineLearning', + label: 'Machine Learning', + group: 'softwareEngineering', + }, + { + id: 'mobileDevelopmentIos', + label: 'iOS Mobile Development', + group: 'softwareEngineering', + }, + { + id: 'mobileDevelopmentAndroid', + label: 'Android Mobile Development', + group: 'softwareEngineering', + }, + { id: 'salesforce', label: 'Salesforce', group: 'softwareEngineering' }, + { + id: 'devOpsCloud', + label: 'DevOps and Cloud (e.g. Azure, AWS)', + group: 'softwareEngineering', + }, + { id: 'iot', label: 'IoT', group: 'softwareEngineering' }, + { + id: 'computerNetworking', + label: 'Computer Networking', + group: 'softwareEngineering', + }, + { id: 'blockchain', label: 'Blockchain', group: 'softwareEngineering' }, + { + id: 'productManagement', + label: 'Product Management', + group: 'otherProfessions', + }, + { + id: 'projectManagement', + label: 'Project Management', + group: 'otherProfessions', + }, + { + id: 'digitalMarketing', + label: 'Digital Marketing', + group: 'otherProfessions', + }, + { + id: 'businessDevelopment', + label: 'Business Development', + group: 'otherProfessions', + }, + { id: 'sales', label: 'Sales', group: 'otherProfessions' }, + { + id: 'qualityAssurance', + label: 'Quality Assurance', + group: 'otherProfessions', + }, + { id: 'basicGerman', label: 'Basic German ๐Ÿ‡ฉ๐Ÿ‡ช', group: 'language' }, + { id: 'businessGerman', label: 'Business German ๐Ÿ‡ฉ๐Ÿ‡ช', group: 'language' }, + { id: 'english', label: 'English ๐Ÿ‡ฌ๐Ÿ‡ง', group: 'language' }, + { id: 'graphicDesign', label: 'Graphic Design', group: 'design' }, + { + id: 'userInterfaceDesign', + label: 'User Interface Design', + group: 'design', + }, + { + id: 'userExperienceDesign', + label: 'User Experience Design', + group: 'design', + }, + { + id: 'motivationAndEncouragement', + label: 'Motivation & encouragement', + group: 'other', + }, + { id: 'friendAndHelp', label: 'Be a friend and help', group: 'other' }, + { id: 'dontKnowYet', label: "I don't know yet", group: 'other' }, + { + id: 'careerOrientationAndPlanning', + label: 'Career orientation & planning', + group: 'careerSupport', + }, + { + id: 'internshipOrWorkingStudent', + label: 'Internship / working student position search', + group: 'careerSupport', + }, + { id: 'jobSearch', label: 'Job search', group: 'careerSupport' }, + { + id: 'jobApplicationsCvPreparationEnglish', + label: 'Job applications and CV preparation in English', + group: 'careerSupport', + }, + { + id: 'jobApplicationsCvPreparationGerman', + label: 'Job applications and CV preparation in German', + group: 'careerSupport', + }, + { + id: 'interviewPreparation', + label: 'Interview preparation', + group: 'careerSupport', + }, + { + id: 'codingChallengePreparation', + label: 'Coding challenge preparation', + group: 'careerSupport', + }, + { + id: 'buildingProfessionalNetwork', + label: 'Building a professional network', + group: 'careerSupport', + }, + { id: 'entrepreneurship', label: 'Entrepreneurship', group: 'careerSupport' }, + { id: 'freelancing', label: 'Freelancing', group: 'careerSupport' }, +] as const +export type CategoryKey = typeof CATEGORIES[number]['id'] +export type CategoryLabel = typeof CATEGORIES[number]['label'] + +export const CATEGORIES_MAP = Object.fromEntries( + CATEGORIES.map((cat) => [cat.id, cat.label]) +) as Record diff --git a/libs/shared-types/src/lib/RedProfile.ts b/libs/shared-types/src/lib/RedProfile.ts index 17e327625..13ed0ab2d 100644 --- a/libs/shared-types/src/lib/RedProfile.ts +++ b/libs/shared-types/src/lib/RedProfile.ts @@ -10,6 +10,8 @@ import { GenderKey, Language, MenteeOccupationCategoryKey, + MentoringTopicKey, + ProfessionalExperienceFieldKey, } from '@talent-connect/shared-config' export type RedProfile = { @@ -51,6 +53,10 @@ export type RedProfile = { favouritedRedProfileIds: Array optOutOfMenteesFromOtherRediLocation: boolean + mentoringTopics: Array + mentoringGoals: Array + professionalExperienceFields: Array + createdAt: Date updatedAt: Date userActivated?: boolean From 5d665c7ffb5566b7914434ffe45288da859db625 Mon Sep 17 00:00:00 2001 From: Eric Bolikowski Date: Wed, 30 Mar 2022 17:58:22 +0200 Subject: [PATCH 04/25] add comment --- libs/shared-config/src/lib/mentoring-data-lists.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/shared-config/src/lib/mentoring-data-lists.ts b/libs/shared-config/src/lib/mentoring-data-lists.ts index f40cf4531..890ad264e 100644 --- a/libs/shared-config/src/lib/mentoring-data-lists.ts +++ b/libs/shared-config/src/lib/mentoring-data-lists.ts @@ -350,6 +350,8 @@ export const MENTORING_TOPICS_MAP = Object.fromEntries( MENTORING_TOPICS.map((topic) => [topic.id, topic.label]) ) as Record +// TODO: these are the **old** 'categories' (i.e. mentoring topics). To be deleted after +// we migrate to the above! export const CATEGORY_GROUPS = { softwareEngineering: '๐Ÿ‘ฉโ€๐Ÿ’ป Software Engineering', design: '๐ŸŽจ Design', From c491402d5f1d6fd49ae65485cb9b86af039815e6 Mon Sep 17 00:00:00 2001 From: Eric Bolikowski Date: Mon, 4 Apr 2022 18:30:45 +0200 Subject: [PATCH 05/25] meh --- apps/redi-connect/src/pages/app/me/Me.tsx | 140 +---------------- .../src/pages/app/me/MeMentorProfile.tsx | 134 ++++++++++++++++ .../EditableMentoringGoal.tsx | 129 ++++++++++++++++ .../me/me-mentee-profile/MeMenteeProfile.tsx | 145 ++++++++++++++++++ .../me-mentee-profile/ReadMentoringGoal.tsx | 56 +++++++ 5 files changed, 471 insertions(+), 133 deletions(-) create mode 100644 apps/redi-connect/src/pages/app/me/MeMentorProfile.tsx create mode 100644 apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoal.tsx create mode 100644 apps/redi-connect/src/pages/app/me/me-mentee-profile/MeMenteeProfile.tsx create mode 100644 apps/redi-connect/src/pages/app/me/me-mentee-profile/ReadMentoringGoal.tsx diff --git a/apps/redi-connect/src/pages/app/me/Me.tsx b/apps/redi-connect/src/pages/app/me/Me.tsx index 528426123..3467ac70a 100644 --- a/apps/redi-connect/src/pages/app/me/Me.tsx +++ b/apps/redi-connect/src/pages/app/me/Me.tsx @@ -1,31 +1,10 @@ +import { Loader } from '@talent-connect/shared-atomic-design-components' import React, { useEffect } from 'react' import { connect } from 'react-redux' import { RootState } from '../../../redux/types' import { profileFetchStart } from '../../../redux/user/actions' -import { Columns, Content, Element } from 'react-bulma-components' -import { - Heading, - Loader, -} from '@talent-connect/shared-atomic-design-components' -import { - Avatar, - EditableAbout, - EditableContactDetails, - EditableEducation, - EditableLanguages, - EditableMentoringTopics, - EditableOccupation, - EditablePersonalDetail, - EditableRediClass, - EditableSocialMedia, - EditableMenteeCount, -} from '../../../components/organisms' - -import { LoggedIn } from '../../../components/templates' -import EditableMentoringTopicsNew2022 from '../../../components/organisms/EditableMentoringTopicsNew2022' -import EditableProfessionalExperienceFields from '../../../components/organisms/EditableProfessionalExperienceFields' -import EditableMentoringGoals from '../../../components/organisms/EditableMentoringGoals' -// CHECK OUT THE LOADER +import MeMenteeProfile from './me-mentee-profile/MeMenteeProfile' +import MeMentorProfile from './MeMentorProfile' const Me = ({ loading, saveResult, profileFetchStart, profile }: any) => { useEffect(() => { @@ -43,115 +22,10 @@ const Me = ({ loading, saveResult, profileFetchStart, profile }: any) => { profile.userType === 'public-sign-up-mentor-pending-review' return ( - - {saveResult === 'error' && <>An error occurred, please try again.} - {saveResult === 'submitting' && } - - - - - - - Hi, {profile.firstName} - - {`Please fill out your profile. Let potential ${ - userIsMentee ? 'mentors' : 'mentees' - } know a little bit more about you, so you can find the perfect fit. If you have filled out your profile: Great! Make sure you keep it up to date.`} - - - - - - {`Please fill out your profile. Let potential ${ - userIsMentee ? 'mentors' : 'mentees' - } know a little bit more about you, so you can find the perfect fit. If you have filled out your profile: Great! Make sure you keep it up to date.`} - - - - - - - {userIsMentor && ( - - - - - - - - )} - - - - - - - - - - - - - - - - - - - - - - - - - - - - {userIsMentee && ( - - - - - - - - - - - - - )} - - - - - - - - - - - - - - - - - - - - - - - - + <> + {userIsMentee ? : null} + {userIsMentor ? : null} + ) } diff --git a/apps/redi-connect/src/pages/app/me/MeMentorProfile.tsx b/apps/redi-connect/src/pages/app/me/MeMentorProfile.tsx new file mode 100644 index 000000000..4ee570284 --- /dev/null +++ b/apps/redi-connect/src/pages/app/me/MeMentorProfile.tsx @@ -0,0 +1,134 @@ +import { + Heading, + Loader, +} from '@talent-connect/shared-atomic-design-components' +import React from 'react' +import { Columns, Content, Element } from 'react-bulma-components' +import { connect } from 'react-redux' +import { + Avatar, + EditableAbout, + EditableContactDetails, + EditableLanguages, + EditableMenteeCount, + EditableOccupation, + EditablePersonalDetail, + EditableSocialMedia, +} from '../../../components/organisms' +import EditableMentoringGoals from '../../../components/organisms/EditableMentoringGoals' +import EditableMentoringTopicsNew2022 from '../../../components/organisms/EditableMentoringTopicsNew2022' +import EditableProfessionalExperienceFields from '../../../components/organisms/EditableProfessionalExperienceFields' +import { LoggedIn } from '../../../components/templates' +import { RootState } from '../../../redux/types' +import { profileFetchStart } from '../../../redux/user/actions' + +const MeMentorProfile = ({ loading, saveResult, profile }: any) => { + if (loading) return + + return ( + + {saveResult === 'error' && <>An error occurred, please try again.} + {saveResult === 'submitting' && } + + + + + + + Hi, {profile.firstName} + + Please fill out your profile. Let potential mentees know a little + bit more about you, so you can find the perfect fit. If you have + filled out your profile: Great! Make sure you keep it up to date. + + + + + + Please fill out your profile. Let potential mentees know a little bit + more about you, so you can find the perfect fit. If you have filled + out your profile: Great! Make sure you keep it up to date. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} + +const mapStateToProps = (state: RootState) => ({ + saveResult: state.user.saveResult, + loading: state.user.loading, + profile: state.user.profile, +}) + +const mapDispatchToProps = (dispatch: any) => ({ + profileFetchStart: () => dispatch(profileFetchStart()), +}) + +export default connect(mapStateToProps, mapDispatchToProps)(MeMentorProfile) diff --git a/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoal.tsx b/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoal.tsx new file mode 100644 index 000000000..d9ce0a5a6 --- /dev/null +++ b/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoal.tsx @@ -0,0 +1,129 @@ +import { + Checkbox, + Editable, +} from '@talent-connect/shared-atomic-design-components' +import { MENTORING_GOALS } from '@talent-connect/shared-config' +import { RedProfile } from '@talent-connect/shared-types' +import { objectEntries } from '@talent-connect/typescript-utilities' +import { FormikValues, useFormik } from 'formik' +import React from 'react' +import { Content, Element } from 'react-bulma-components' +import { connect } from 'react-redux' +import * as Yup from 'yup' +import { RootState } from '../../../../redux/types' +import { profileSaveStart } from '../../../../redux/user/actions' +import ReadMentoringGoal from './ReadMentoringGoal' + +export interface FormValues { + mentoringGoals: string[] +} + +const formMentoringGoals = objectEntries(MENTORING_GOALS) + +interface Props { + profile: RedProfile | undefined + profileSaveStart: Function +} + +const validationSchema = Yup.object({ + mentoringGoals: Yup.array().min(1, 'You need to select one mentoring goal'), +}) + +const EditableMentoringGoal = ({ profile, profileSaveStart }: Props) => { + const { id, mentoringGoals } = profile as RedProfile + + const submitForm = async (values: FormikValues) => { + const profileMentoring = values as Partial + profileSaveStart({ ...profileMentoring, id }) + } + + const initialValues: FormValues = { + mentoringGoals: mentoringGoals || [], + } + + const formik = useFormik({ + initialValues, + enableReinitialize: true, + validationSchema, + onSubmit: submitForm, + }) + + const { mentoringGoals: selectedMentoringGoals } = formik.values + + const mentoringGoalsChange = (e: any) => { + e.persist() + const value = e.target.value + let newMentoringGoals + if (e.target.checked) { + newMentoringGoals = selectedMentoringGoals.concat(value) + } else { + newMentoringGoals = selectedMentoringGoals.filter( + (cat: any) => cat !== value + ) + } + formik.setFieldValue('mentoringGoals', newMentoringGoals) + formik.setFieldTouched('mentoringGoals', true, false) + } + + return ( + formik.handleSubmit()} + onClose={() => formik.resetForm()} + savePossible={formik.dirty && formik.isValid} + read={} + className="mentoring" + > + + Select at least one goal you would like to support mentees with + + + {formMentoringGoals.map(([fieldId, fieldLabel]) => ( + + ))} + + + ) +} + +const MentoringGoal = ({ + id, + label, + selectedMentoringGoals, + onChange, + formik, +}: any) => { + return ( + + {label} + + ) +} + +const mapStateToProps = (state: RootState) => ({ + profile: state.user.profile, +}) + +const mapDispatchToProps = (dispatch: any) => ({ + profileSaveStart: (profile: Partial) => + dispatch(profileSaveStart(profile)), +}) + +export default connect( + mapStateToProps, + mapDispatchToProps +)(EditableMentoringGoal) diff --git a/apps/redi-connect/src/pages/app/me/me-mentee-profile/MeMenteeProfile.tsx b/apps/redi-connect/src/pages/app/me/me-mentee-profile/MeMenteeProfile.tsx new file mode 100644 index 000000000..d78d18e55 --- /dev/null +++ b/apps/redi-connect/src/pages/app/me/me-mentee-profile/MeMenteeProfile.tsx @@ -0,0 +1,145 @@ +import { + Heading, + Loader, +} from '@talent-connect/shared-atomic-design-components' +import React from 'react' +import { Columns, Content, Element } from 'react-bulma-components' +import { connect } from 'react-redux' +import { + Avatar, + EditableAbout, + EditableContactDetails, + EditableEducation, + EditableLanguages, + EditableOccupation, + EditablePersonalDetail, + EditableRediClass, + EditableSocialMedia, +} from '../../../../components/organisms' +import { LoggedIn } from '../../../../components/templates' +import { RootState } from '../../../../redux/types' +import { profileFetchStart } from '../../../../redux/user/actions' +import EditableMentoringGoal from './EditableMentoringGoal' + +const MeMenteeProfile = ({ loading, saveResult, profile }: any) => { + if (loading) return + + return ( + + {saveResult === 'error' && <>An error occurred, please try again.} + {saveResult === 'submitting' && } + + + + + + + Hi, {profile.firstName} + + Please fill out your profile. Let potential mentors know a little + bit more about you, so you can find the perfect fit. If you have + filled out your profile: Great! Make sure you keep it up to date. + + + + + + Please fill out your profile. Let potential mentors know a little bit + more about you, so you can find the perfect fit. If you have filled + out your profile: Great! Make sure you keep it up to date. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Mentoring Goals and Topics + + + + + + + + + + + + {/* */} + + + ) +} + +const mapStateToProps = (state: RootState) => ({ + saveResult: state.user.saveResult, + loading: state.user.loading, + profile: state.user.profile, +}) + +const mapDispatchToProps = (dispatch: any) => ({ + profileFetchStart: () => dispatch(profileFetchStart()), +}) + +export default connect(mapStateToProps, mapDispatchToProps)(MeMenteeProfile) diff --git a/apps/redi-connect/src/pages/app/me/me-mentee-profile/ReadMentoringGoal.tsx b/apps/redi-connect/src/pages/app/me/me-mentee-profile/ReadMentoringGoal.tsx new file mode 100644 index 000000000..0a01e22b3 --- /dev/null +++ b/apps/redi-connect/src/pages/app/me/me-mentee-profile/ReadMentoringGoal.tsx @@ -0,0 +1,56 @@ +import { + Caption, + CardTags, + CardTagsProps, + Placeholder, +} from '@talent-connect/shared-atomic-design-components' +import { MENTORING_GOALS } from '@talent-connect/shared-config' +import { RedProfile } from '@talent-connect/shared-types' +import React from 'react' +import { connect } from 'react-redux' +import { RootState } from '../../../../redux/types' + +interface ReadMentoringProps { + profile: RedProfile + caption?: boolean +} + +export const ProfileTags = ({ items, shortList }: CardTagsProps) => ( + MENTORING_GOALS[item]} + /> +) + +const ReadMentoringGoal = ({ profile, caption }: ReadMentoringProps) => { + const { mentoringGoals } = profile + + if (!mentoringGoals?.length && !caption) + return ( + + Select at least one goal you would like to support mentees with + + ) + + return ( + <> + {caption && Mentoring goals} + + + ) +} + +const mapStateToProps = (state: RootState) => ({ + profile: state.user.profile as RedProfile, +}) + +export default { + Me: connect(mapStateToProps, {})(ReadMentoringGoal), + Some: ({ profile }: ReadMentoringProps) => ( + + ), + Tags: ({ items, shortList }: CardTagsProps) => ( + + ), +} From 934dcc24ee46d2bc3f28114fabd1c580ae87dc41 Mon Sep 17 00:00:00 2001 From: Eric Bolikowski Date: Thu, 9 Jun 2022 17:34:57 +0200 Subject: [PATCH 06/25] update mentee profile --- .../molecules/ReadMentoringGoals.tsx | 2 +- .../molecules/ReadMentoringTopicsNew2022.tsx | 6 +- .../ReadProfessionalExperienceFields.tsx | 7 +- .../components/organisms/EditableAbout.tsx | 1 + .../organisms/EditableMentoringGoals.tsx | 6 +- .../EditableMentoringTopicsNew2022.tsx | 14 +- .../EditableProfessionalExperienceFields.tsx | 12 +- .../EditableMentoringGoal.tsx | 129 ----- .../EditableMentoringGoalTopics.tsx | 215 ++++++++ .../EditableToolsAndFrameworks.tsx | 140 ++++++ .../me/me-mentee-profile/MeMenteeProfile.tsx | 45 +- .../me-mentee-profile/ReadMentoringGoal.tsx | 56 --- .../ReadMentoringGoalTopics.tsx | 137 ++++++ .../ReadToolsAndFrameworks.tsx | 46 ++ apps/redi-connect/src/redux/matches/epics.ts | 1 - apps/redi-connect/src/styles/_global.scss | 32 ++ .../src/lib/atoms/Caption.tsx | 7 +- .../src/lib/molecules/Editable.tsx | 4 +- .../src/lib/styles/_global.scss | 32 ++ .../src/lib/mentoring-data-lists.ts | 457 ++++++++++-------- libs/shared-types/src/lib/RedProfile.ts | 17 +- 21 files changed, 928 insertions(+), 438 deletions(-) delete mode 100644 apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoal.tsx create mode 100644 apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoalTopics.tsx create mode 100644 apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableToolsAndFrameworks.tsx delete mode 100644 apps/redi-connect/src/pages/app/me/me-mentee-profile/ReadMentoringGoal.tsx create mode 100644 apps/redi-connect/src/pages/app/me/me-mentee-profile/ReadMentoringGoalTopics.tsx create mode 100644 apps/redi-connect/src/pages/app/me/me-mentee-profile/ReadToolsAndFrameworks.tsx diff --git a/apps/redi-connect/src/components/molecules/ReadMentoringGoals.tsx b/apps/redi-connect/src/components/molecules/ReadMentoringGoals.tsx index c3990d037..35ce4f58d 100644 --- a/apps/redi-connect/src/components/molecules/ReadMentoringGoals.tsx +++ b/apps/redi-connect/src/components/molecules/ReadMentoringGoals.tsx @@ -24,7 +24,7 @@ export const ProfileTags = ({ items, shortList }: CardTagsProps) => ( ) const ReadMentoringGoals = ({ profile, caption }: ReadMentoringProps) => { - const { mentoringGoals } = profile + const { mentor_mentoringGoals: mentoringGoals } = profile if (!mentoringGoals?.length && !caption) return ( diff --git a/apps/redi-connect/src/components/molecules/ReadMentoringTopicsNew2022.tsx b/apps/redi-connect/src/components/molecules/ReadMentoringTopicsNew2022.tsx index 48f62fadd..5ed9a378c 100644 --- a/apps/redi-connect/src/components/molecules/ReadMentoringTopicsNew2022.tsx +++ b/apps/redi-connect/src/components/molecules/ReadMentoringTopicsNew2022.tsx @@ -27,15 +27,15 @@ const ReadMentoringTopicsNew2022 = ({ profile, caption, }: ReadMentoringProps) => { - const { mentoringTopics } = profile + const { mentor_mentoringTopics } = profile - if (!mentoringTopics?.length && !caption) + if (!mentor_mentoringTopics?.length && !caption) return Please pick mentoring topics. return ( <> {caption && {'Mentoring Topics'}} - + ) } diff --git a/apps/redi-connect/src/components/molecules/ReadProfessionalExperienceFields.tsx b/apps/redi-connect/src/components/molecules/ReadProfessionalExperienceFields.tsx index cb0310a3c..f13a804a9 100644 --- a/apps/redi-connect/src/components/molecules/ReadProfessionalExperienceFields.tsx +++ b/apps/redi-connect/src/components/molecules/ReadProfessionalExperienceFields.tsx @@ -4,7 +4,7 @@ import { CardTagsProps, Placeholder, } from '@talent-connect/shared-atomic-design-components' -import { PROFESSIONAL_EXPERIENCE_FIELDS } from '@talent-connect/shared-config' +import { FIELDS_OF_EXPERTISE } from '@talent-connect/shared-config' import { RedProfile } from '@talent-connect/shared-types' import React from 'react' import { connect } from 'react-redux' @@ -19,7 +19,7 @@ export const ProfileTags = ({ items, shortList }: CardTagsProps) => ( PROFESSIONAL_EXPERIENCE_FIELDS[item]} + formatter={(item: string) => FIELDS_OF_EXPERTISE[item]} /> ) @@ -27,7 +27,8 @@ const ReadProfessionalExperienceFields = ({ profile, caption, }: ReadMentoringProps) => { - const { professionalExperienceFields } = profile + const { mentor_professionalExperienceFields: professionalExperienceFields } = + profile if (!professionalExperienceFields?.length && !caption) return Select your fields of expertise diff --git a/apps/redi-connect/src/components/organisms/EditableAbout.tsx b/apps/redi-connect/src/components/organisms/EditableAbout.tsx index 1692471a0..e6397df05 100644 --- a/apps/redi-connect/src/components/organisms/EditableAbout.tsx +++ b/apps/redi-connect/src/components/organisms/EditableAbout.tsx @@ -58,6 +58,7 @@ const EditableAbout = ({ profile, profileSaveStart }: any) => { return ( formik.handleSubmit()} onClose={() => formik.resetForm()} savePossible={formik.dirty && formik.isValid} diff --git a/apps/redi-connect/src/components/organisms/EditableMentoringGoals.tsx b/apps/redi-connect/src/components/organisms/EditableMentoringGoals.tsx index 1d8901c6e..b51668d72 100644 --- a/apps/redi-connect/src/components/organisms/EditableMentoringGoals.tsx +++ b/apps/redi-connect/src/components/organisms/EditableMentoringGoals.tsx @@ -31,7 +31,11 @@ const validationSchema = Yup.object({ }) const EditableMentoringGoals = ({ profile, profileSaveStart }: Props) => { - const { id, userType, mentoringGoals } = profile as RedProfile + const { + id, + userType, + mentor_mentoringGoals: mentoringGoals, + } = profile as RedProfile const submitForm = async (values: FormikValues) => { const profileMentoring = values as Partial diff --git a/apps/redi-connect/src/components/organisms/EditableMentoringTopicsNew2022.tsx b/apps/redi-connect/src/components/organisms/EditableMentoringTopicsNew2022.tsx index 6f50ad1c1..fb2f1bb3a 100644 --- a/apps/redi-connect/src/components/organisms/EditableMentoringTopicsNew2022.tsx +++ b/apps/redi-connect/src/components/organisms/EditableMentoringTopicsNew2022.tsx @@ -25,7 +25,7 @@ export type UserType = | 'public-sign-up-mentee-pending-review' export interface FormValues { - mentoringTopics: MentoringTopicKey[] + mentor_mentoringTopics: MentoringTopicKey[] } const mentoringTopicsByGroup = groupBy(MENTORING_TOPICS, (topic) => topic.group) @@ -38,7 +38,7 @@ interface Props { } const EditableMentoringTopics = ({ profile, profileSaveStart }: Props) => { - const { id, userType, mentoringTopics } = profile as RedProfile + const { id, userType, mentor_mentoringTopics } = profile as RedProfile const submitForm = async (values: FormikValues) => { const profileMentoring = values as Partial @@ -46,7 +46,7 @@ const EditableMentoringTopics = ({ profile, profileSaveStart }: Props) => { } const initialValues: FormValues = { - mentoringTopics: mentoringTopics || [], + mentor_mentoringTopics: mentor_mentoringTopics || [], } const formik = useFormik({ @@ -55,7 +55,7 @@ const EditableMentoringTopics = ({ profile, profileSaveStart }: Props) => { onSubmit: submitForm, }) - const { mentoringTopics: selectedMentoringTopics } = formik.values + const { mentor_mentoringTopics: selectedMentoringTopics } = formik.values const mentoringTopicsChange = (e: any) => { e.persist() @@ -68,8 +68,8 @@ const EditableMentoringTopics = ({ profile, profileSaveStart }: Props) => { (cat: any) => cat !== value ) } - formik.setFieldValue('mentoringTopics', newMentoringTopics) - formik.setFieldTouched('mentoringTopics', true, false) + formik.setFieldValue('mentor_mentoringTopics', newMentoringTopics) + formik.setFieldTouched('mentor_mentoringTopics', true, false) } return ( @@ -122,7 +122,7 @@ const MentoringTopicGroup = ({ {mentoringTopicsByGroup[id].map((groupItem) => ( { - const { id, userType, professionalExperienceFields } = profile as RedProfile + const { + id, + userType, + mentor_professionalExperienceFields: professionalExperienceFields, + } = profile as RedProfile const submitForm = async (values: FormikValues) => { const profileMentoring = values as Partial diff --git a/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoal.tsx b/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoal.tsx deleted file mode 100644 index d9ce0a5a6..000000000 --- a/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoal.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import { - Checkbox, - Editable, -} from '@talent-connect/shared-atomic-design-components' -import { MENTORING_GOALS } from '@talent-connect/shared-config' -import { RedProfile } from '@talent-connect/shared-types' -import { objectEntries } from '@talent-connect/typescript-utilities' -import { FormikValues, useFormik } from 'formik' -import React from 'react' -import { Content, Element } from 'react-bulma-components' -import { connect } from 'react-redux' -import * as Yup from 'yup' -import { RootState } from '../../../../redux/types' -import { profileSaveStart } from '../../../../redux/user/actions' -import ReadMentoringGoal from './ReadMentoringGoal' - -export interface FormValues { - mentoringGoals: string[] -} - -const formMentoringGoals = objectEntries(MENTORING_GOALS) - -interface Props { - profile: RedProfile | undefined - profileSaveStart: Function -} - -const validationSchema = Yup.object({ - mentoringGoals: Yup.array().min(1, 'You need to select one mentoring goal'), -}) - -const EditableMentoringGoal = ({ profile, profileSaveStart }: Props) => { - const { id, mentoringGoals } = profile as RedProfile - - const submitForm = async (values: FormikValues) => { - const profileMentoring = values as Partial - profileSaveStart({ ...profileMentoring, id }) - } - - const initialValues: FormValues = { - mentoringGoals: mentoringGoals || [], - } - - const formik = useFormik({ - initialValues, - enableReinitialize: true, - validationSchema, - onSubmit: submitForm, - }) - - const { mentoringGoals: selectedMentoringGoals } = formik.values - - const mentoringGoalsChange = (e: any) => { - e.persist() - const value = e.target.value - let newMentoringGoals - if (e.target.checked) { - newMentoringGoals = selectedMentoringGoals.concat(value) - } else { - newMentoringGoals = selectedMentoringGoals.filter( - (cat: any) => cat !== value - ) - } - formik.setFieldValue('mentoringGoals', newMentoringGoals) - formik.setFieldTouched('mentoringGoals', true, false) - } - - return ( - formik.handleSubmit()} - onClose={() => formik.resetForm()} - savePossible={formik.dirty && formik.isValid} - read={} - className="mentoring" - > - - Select at least one goal you would like to support mentees with - - - {formMentoringGoals.map(([fieldId, fieldLabel]) => ( - - ))} - - - ) -} - -const MentoringGoal = ({ - id, - label, - selectedMentoringGoals, - onChange, - formik, -}: any) => { - return ( - - {label} - - ) -} - -const mapStateToProps = (state: RootState) => ({ - profile: state.user.profile, -}) - -const mapDispatchToProps = (dispatch: any) => ({ - profileSaveStart: (profile: Partial) => - dispatch(profileSaveStart(profile)), -}) - -export default connect( - mapStateToProps, - mapDispatchToProps -)(EditableMentoringGoal) diff --git a/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoalTopics.tsx b/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoalTopics.tsx new file mode 100644 index 000000000..f52e20e63 --- /dev/null +++ b/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoalTopics.tsx @@ -0,0 +1,215 @@ +import { + Caption, + Editable, + FormSelect, +} from '@talent-connect/shared-atomic-design-components' +import { + DESIRED_ROLES, + MENTORING_GOALS, + MENTORING_TOPICS, +} from '@talent-connect/shared-config' +import { RedProfile } from '@talent-connect/shared-types' +import { objectEntries } from '@talent-connect/typescript-utilities' +import { FormikValues, useFormik } from 'formik' +import React, { useEffect } from 'react' +import { Columns, Element } from 'react-bulma-components' +import { connect } from 'react-redux' +import * as Yup from 'yup' +import { RootState } from '../../../../redux/types' +import { profileSaveStart } from '../../../../redux/user/actions' +import ReadMentoringGoalTopics from './ReadMentoringGoalTopics' + +type FormValues = Pick< + RedProfile, + | 'mentee_mentoringGoal' + | 'mentee_overarchingMentoringTopics' + | 'mentee_primaryRole_fieldOfExpertise' + | 'mentee_primaryRole_mentoringTopics' + | 'mentee_secondaryRole_fieldOfExpertise' + | 'mentee_secondaryRole_mentoringTopics' +> + +interface Props { + profile: RedProfile | undefined + profileSaveStart: Function +} + +const validationSchema = Yup.object({ + mentee_mentoringGoal: Yup.string() + .nullable() + .required('Select a mentoring goal'), + mentee_primaryRole_fieldOfExpertise: Yup.string() + .nullable() + .required('Select a role'), +}) + +const EditableMentoringGoalTopics = ({ profile, profileSaveStart }: Props) => { + const { + id, + mentee_mentoringGoal, + mentee_overarchingMentoringTopics = [], + mentee_primaryRole_fieldOfExpertise, + mentee_primaryRole_mentoringTopics = [], + mentee_secondaryRole_fieldOfExpertise, + mentee_secondaryRole_mentoringTopics = [], + } = profile as RedProfile + + const submitForm = async (values: FormikValues) => { + const profileMentoring = values as Partial + profileSaveStart({ ...profileMentoring, id }) + } + + const initialValues: FormValues = { + mentee_mentoringGoal, + mentee_overarchingMentoringTopics, + mentee_primaryRole_fieldOfExpertise, + mentee_primaryRole_mentoringTopics, + mentee_secondaryRole_fieldOfExpertise, + mentee_secondaryRole_mentoringTopics, + } + + const formik = useFormik({ + initialValues, + enableReinitialize: true, + validationSchema, + onSubmit: submitForm, + }) + + useEffect(() => { + if (mentee_primaryRole_fieldOfExpertise) + formik.setFieldValue('mentee_primaryRole_mentoringTopics', []) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [formik.values.mentee_primaryRole_fieldOfExpertise]) + + useEffect(() => { + if (mentee_secondaryRole_fieldOfExpertise) + formik.setFieldValue('mentee_secondaryRole_mentoringTopics', []) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [formik.values.mentee_secondaryRole_fieldOfExpertise]) + + return ( + <> + + formik.handleSubmit()} + onClose={() => formik.resetForm()} + savePossible={formik.dirty && formik.isValid} + read={} + className="mentoring" + > + + + + Goal + + + + Topics + + + + + + + + Primary role + + + + Skills + + + + + + + + Secondary role + + + + Skills + + + + + + + ) +} + +const formMentoringGoals = objectEntries(MENTORING_GOALS).map( + ([value, label]) => ({ value, label }) +) +const formFieldsOfExpertise = objectEntries(DESIRED_ROLES).map( + ([value, label]) => ({ value, label }) +) +const formMentoringTopicsInGroup = (group) => + MENTORING_TOPICS.filter((topic) => topic.group === group).map( + ({ id, label }) => ({ value: id, label }) + ) + +const mapStateToProps = (state: RootState) => ({ + profile: state.user.profile, +}) +const mapDispatchToProps = (dispatch: any) => ({ + profileSaveStart: (profile: Partial) => + dispatch(profileSaveStart(profile)), +}) + +export default connect( + mapStateToProps, + mapDispatchToProps +)(EditableMentoringGoalTopics) diff --git a/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableToolsAndFrameworks.tsx b/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableToolsAndFrameworks.tsx new file mode 100644 index 000000000..92b4a3948 --- /dev/null +++ b/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableToolsAndFrameworks.tsx @@ -0,0 +1,140 @@ +import { + Caption, + Checkbox, + Editable, + Placeholder, +} from '@talent-connect/shared-atomic-design-components' +import { MENTORING_TOPICS } from '@talent-connect/shared-config' +import { RedProfile } from '@talent-connect/shared-types' +import { FormikValues, useFormik } from 'formik' +import { chunk } from 'lodash' +import React from 'react' +import { Columns } from 'react-bulma-components' +import { connect } from 'react-redux' +import * as Yup from 'yup' +import { RootState } from '../../../../redux/types' +import { profileSaveStart } from '../../../../redux/user/actions' +import ReadToolsAndFrameworks from './ReadToolsAndFrameworks' + +type FormValues = Pick + +interface Props { + profile: RedProfile | undefined + profileSaveStart: Function +} + +const validationSchema = Yup.object({ + mentee_toolsAndFrameworks_mentoringTopics: Yup.array().max( + 3, + 'You can select up to 3 tools and frameworks' + ), +}) + +const EditableToolsAndFrameworks = ({ profile, profileSaveStart }: Props) => { + const { id, mentee_toolsAndFrameworks_mentoringTopics } = + profile as RedProfile + + const submitForm = async (values: FormikValues) => { + const profileMentoring = values as Partial + profileSaveStart({ ...profileMentoring, id }) + } + + const initialValues: FormValues = { + mentee_toolsAndFrameworks_mentoringTopics: + mentee_toolsAndFrameworks_mentoringTopics ?? [], + } + + const formik = useFormik({ + initialValues, + enableReinitialize: true, + validationSchema, + onSubmit: submitForm, + }) + + const selectedValues = formik.values.mentee_toolsAndFrameworks_mentoringTopics + + const onChange = (e: any) => { + e.persist() + const value = e.target.value + let newCategories + if (e.target.checked) { + newCategories = selectedValues.concat(value) + } else { + newCategories = selectedValues.filter((cat: any) => cat !== value) + } + formik.setFieldValue( + 'mentee_toolsAndFrameworks_mentoringTopics', + newCategories + ) + formik.setFieldTouched( + 'mentee_toolsAndFrameworks_mentoringTopics', + true, + false + ) + } + + return ( + <> + + formik.handleSubmit()} + onClose={() => formik.resetForm()} + savePossible={formik.dirty && formik.isValid} + read={} + className="mentoring" + > + + Please select tool and technologies you are particularly interested in + (max 3). + + + {formToolsAndFrameworksGroups.map((group) => ( + + {group.map((item) => ( + = 3 && + !selectedValues.includes(item.value) + } + {...formik} + > + {item.label} + + ))} + + ))} + + + + ) +} + +const formToolsAndFrameworks = MENTORING_TOPICS.filter( + (topic) => topic.group === 'toolsAndFrameworks' +) + .map(({ id, label }) => ({ value: id, label })) + .sort((a, b) => (a.label > b.label ? 1 : -1)) +const formToolsAndFrameworksGroups = chunk( + formToolsAndFrameworks, + Math.ceil(formToolsAndFrameworks.length / 3) +) + +const mapStateToProps = (state: RootState) => ({ + profile: state.user.profile, +}) +const mapDispatchToProps = (dispatch: any) => ({ + profileSaveStart: (profile: Partial) => + dispatch(profileSaveStart(profile)), +}) + +export default connect( + mapStateToProps, + mapDispatchToProps +)(EditableToolsAndFrameworks) diff --git a/apps/redi-connect/src/pages/app/me/me-mentee-profile/MeMenteeProfile.tsx b/apps/redi-connect/src/pages/app/me/me-mentee-profile/MeMenteeProfile.tsx index d78d18e55..5ff182a16 100644 --- a/apps/redi-connect/src/pages/app/me/me-mentee-profile/MeMenteeProfile.tsx +++ b/apps/redi-connect/src/pages/app/me/me-mentee-profile/MeMenteeProfile.tsx @@ -1,4 +1,5 @@ import { + Caption, Heading, Loader, } from '@talent-connect/shared-atomic-design-components' @@ -19,7 +20,8 @@ import { import { LoggedIn } from '../../../../components/templates' import { RootState } from '../../../../redux/types' import { profileFetchStart } from '../../../../redux/user/actions' -import EditableMentoringGoal from './EditableMentoringGoal' +import EditableMentoringGoalTopics from './EditableMentoringGoalTopics' +import EditableToolsAndFrameworks from './EditableToolsAndFrameworks' const MeMenteeProfile = ({ loading, saveResult, profile }: any) => { if (loading) return @@ -47,7 +49,7 @@ const MeMenteeProfile = ({ loading, saveResult, profile }: any) => { @@ -56,14 +58,15 @@ const MeMenteeProfile = ({ loading, saveResult, profile }: any) => { out your profile: Great! Make sure you keep it up to date. - + - + + Professional experience and contact details - + @@ -73,10 +76,10 @@ const MeMenteeProfile = ({ loading, saveResult, profile }: any) => { - + - + @@ -86,10 +89,10 @@ const MeMenteeProfile = ({ loading, saveResult, profile }: any) => { - + - + @@ -99,7 +102,7 @@ const MeMenteeProfile = ({ loading, saveResult, profile }: any) => { - + @@ -107,26 +110,12 @@ const MeMenteeProfile = ({ loading, saveResult, profile }: any) => { - - - Mentoring Goals and Topics - - - - - - - - + + - - {/* */} + + ) diff --git a/apps/redi-connect/src/pages/app/me/me-mentee-profile/ReadMentoringGoal.tsx b/apps/redi-connect/src/pages/app/me/me-mentee-profile/ReadMentoringGoal.tsx deleted file mode 100644 index 0a01e22b3..000000000 --- a/apps/redi-connect/src/pages/app/me/me-mentee-profile/ReadMentoringGoal.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { - Caption, - CardTags, - CardTagsProps, - Placeholder, -} from '@talent-connect/shared-atomic-design-components' -import { MENTORING_GOALS } from '@talent-connect/shared-config' -import { RedProfile } from '@talent-connect/shared-types' -import React from 'react' -import { connect } from 'react-redux' -import { RootState } from '../../../../redux/types' - -interface ReadMentoringProps { - profile: RedProfile - caption?: boolean -} - -export const ProfileTags = ({ items, shortList }: CardTagsProps) => ( - MENTORING_GOALS[item]} - /> -) - -const ReadMentoringGoal = ({ profile, caption }: ReadMentoringProps) => { - const { mentoringGoals } = profile - - if (!mentoringGoals?.length && !caption) - return ( - - Select at least one goal you would like to support mentees with - - ) - - return ( - <> - {caption && Mentoring goals} - - - ) -} - -const mapStateToProps = (state: RootState) => ({ - profile: state.user.profile as RedProfile, -}) - -export default { - Me: connect(mapStateToProps, {})(ReadMentoringGoal), - Some: ({ profile }: ReadMentoringProps) => ( - - ), - Tags: ({ items, shortList }: CardTagsProps) => ( - - ), -} diff --git a/apps/redi-connect/src/pages/app/me/me-mentee-profile/ReadMentoringGoalTopics.tsx b/apps/redi-connect/src/pages/app/me/me-mentee-profile/ReadMentoringGoalTopics.tsx new file mode 100644 index 000000000..906037dbf --- /dev/null +++ b/apps/redi-connect/src/pages/app/me/me-mentee-profile/ReadMentoringGoalTopics.tsx @@ -0,0 +1,137 @@ +import { + Caption, + CardTags, + Placeholder, +} from '@talent-connect/shared-atomic-design-components' +import { + FIELDS_OF_EXPERTISE, + MENTORING_GOALS, + MENTORING_TOPICS_MAP, +} from '@talent-connect/shared-config' +import { RedProfile } from '@talent-connect/shared-types' +import React from 'react' +import { Columns, Element } from 'react-bulma-components' +import { connect } from 'react-redux' +import { RootState } from '../../../../redux/types' + +interface ReadMentoringProps { + profile: RedProfile + caption?: boolean +} + +const ReadMentoringGoalTopics = ({ profile, caption }: ReadMentoringProps) => { + const { + mentee_mentoringGoal, + mentee_overarchingMentoringTopics = [], + mentee_primaryRole_fieldOfExpertise, + mentee_primaryRole_mentoringTopics = [], + mentee_secondaryRole_fieldOfExpertise, + mentee_secondaryRole_mentoringTopics = [], + } = profile + + return ( + <> + + + + Goal + {mentee_mentoringGoal ? ( + + ) : ( + + The most important goal you would like to adress with your + mentor + + )} + + + Topics + {mentee_overarchingMentoringTopics.length > 0 ? ( + MENTORING_TOPICS_MAP[item]} + /> + ) : ( + + General topics you would like to me mentored on + + )} + + + + + + + Primary role + {mentee_primaryRole_fieldOfExpertise ? ( + + ) : ( + + The primary role you would like to be mentored on. + + )} + + + Skills + {mentee_primaryRole_mentoringTopics.length > 0 ? ( + MENTORING_TOPICS_MAP[item]} + /> + ) : ( + + Role-related skills you would like to mentored on (optional) + + )} + + + + + + + Secondary role (optional) + {mentee_secondaryRole_fieldOfExpertise ? ( + + ) : ( + + The secondary role you would like to be mentored on. + + )} + + + Skills + {mentee_secondaryRole_mentoringTopics.length > 0 ? ( + MENTORING_TOPICS_MAP[item]} + /> + ) : ( + + Role-related skills you would like to mentored on (optional) + + )} + + + + + ) +} + +const mapStateToProps = (state: RootState) => ({ + profile: state.user.profile as RedProfile, +}) + +export default { + Me: connect(mapStateToProps, {})(ReadMentoringGoalTopics), + Some: ({ profile }: ReadMentoringProps) => ( + + ), +} diff --git a/apps/redi-connect/src/pages/app/me/me-mentee-profile/ReadToolsAndFrameworks.tsx b/apps/redi-connect/src/pages/app/me/me-mentee-profile/ReadToolsAndFrameworks.tsx new file mode 100644 index 000000000..686d5d4f0 --- /dev/null +++ b/apps/redi-connect/src/pages/app/me/me-mentee-profile/ReadToolsAndFrameworks.tsx @@ -0,0 +1,46 @@ +import { + CardTags, + Placeholder, +} from '@talent-connect/shared-atomic-design-components' +import { MENTORING_TOPICS_MAP } from '@talent-connect/shared-config' +import { RedProfile } from '@talent-connect/shared-types' +import React from 'react' +import { Element } from 'react-bulma-components' +import { connect } from 'react-redux' +import { RootState } from '../../../../redux/types' + +interface ReadMentoringProps { + profile: RedProfile + caption?: boolean +} + +const ReadToolsAndFrameworks = ({ profile, caption }: ReadMentoringProps) => { + const { mentee_toolsAndFrameworks_mentoringTopics = [] } = profile + + return ( + + {mentee_toolsAndFrameworks_mentoringTopics.length > 0 ? ( + MENTORING_TOPICS_MAP[item]} + /> + ) : ( + + Please select tool and technologies you are particularly interested in + (max 3). + + )} + + ) +} + +const mapStateToProps = (state: RootState) => ({ + profile: state.user.profile as RedProfile, +}) + +export default { + Me: connect(mapStateToProps, {})(ReadToolsAndFrameworks), + Some: ({ profile }: ReadMentoringProps) => ( + + ), +} diff --git a/apps/redi-connect/src/redux/matches/epics.ts b/apps/redi-connect/src/redux/matches/epics.ts index 3dee161dc..5c2334ddc 100644 --- a/apps/redi-connect/src/redux/matches/epics.ts +++ b/apps/redi-connect/src/redux/matches/epics.ts @@ -96,7 +96,6 @@ export const matchesAcceptMentorshipEpic = (action$: ActionsObservable) => export const matchesDeclineMentorshipEpic = (action$: ActionsObservable) => action$.pipe( - tap((p) => console.log('Hello hello', p)), ofType(MatchesActionType.MATCHES_DECLINE_MENTORSHIP_START), switchMap((action) => { const request = from( diff --git a/apps/redi-connect/src/styles/_global.scss b/apps/redi-connect/src/styles/_global.scss index 8e1cdbc18..9cc13d8e1 100644 --- a/apps/redi-connect/src/styles/_global.scss +++ b/apps/redi-connect/src/styles/_global.scss @@ -16,3 +16,35 @@ border-bottom: 1px solid $grey-light; } } + +.block-thicker-separator { + .block-thicker-separator { + @include mobile() { + padding-bottom: 1.5rem; + margin-bottom: -0.5rem; + border-bottom: 2px solid $grey-light; + } + } + + &:not(:last-child) { + padding-bottom: 1.5rem; + margin-bottom: 1.5rem; + border-bottom: 2px solid $grey-light; + } +} + +.block-thicker-separator-dashed { + .block-thicker-separator-dashed { + @include mobile() { + padding-bottom: 1.5rem; + margin-bottom: -0.5rem; + border-bottom: 2px dashed $grey-light; + } + } + + &:not(:last-child) { + padding-bottom: 1.5rem; + margin-bottom: 1.5rem; + border-bottom: 2px dashed $grey-light; + } +} diff --git a/libs/shared-atomic-design-components/src/lib/atoms/Caption.tsx b/libs/shared-atomic-design-components/src/lib/atoms/Caption.tsx index 22ad04df8..8407d593e 100644 --- a/libs/shared-atomic-design-components/src/lib/atoms/Caption.tsx +++ b/libs/shared-atomic-design-components/src/lib/atoms/Caption.tsx @@ -2,10 +2,13 @@ import React from 'react' import { Heading as BulmaHeading } from 'react-bulma-components' import './Caption.scss' -const Caption: React.FunctionComponent = ({ children }) => ( +const Caption: React.FunctionComponent<{ bold?: boolean }> = ({ + bold = false, + children, +}) => ( void onClose: () => void read: React.ReactNode @@ -17,6 +18,7 @@ interface Props { function Editable(props: Props) { const { title, + titleBold, children, read, onSave, @@ -40,7 +42,7 @@ function Editable(props: Props) { return (
- {title} + {title}
{isEditing ? ( <> diff --git a/libs/shared-atomic-design-components/src/lib/styles/_global.scss b/libs/shared-atomic-design-components/src/lib/styles/_global.scss index 8e1cdbc18..9cc13d8e1 100644 --- a/libs/shared-atomic-design-components/src/lib/styles/_global.scss +++ b/libs/shared-atomic-design-components/src/lib/styles/_global.scss @@ -16,3 +16,35 @@ border-bottom: 1px solid $grey-light; } } + +.block-thicker-separator { + .block-thicker-separator { + @include mobile() { + padding-bottom: 1.5rem; + margin-bottom: -0.5rem; + border-bottom: 2px solid $grey-light; + } + } + + &:not(:last-child) { + padding-bottom: 1.5rem; + margin-bottom: 1.5rem; + border-bottom: 2px solid $grey-light; + } +} + +.block-thicker-separator-dashed { + .block-thicker-separator-dashed { + @include mobile() { + padding-bottom: 1.5rem; + margin-bottom: -0.5rem; + border-bottom: 2px dashed $grey-light; + } + } + + &:not(:last-child) { + padding-bottom: 1.5rem; + margin-bottom: 1.5rem; + border-bottom: 2px dashed $grey-light; + } +} diff --git a/libs/shared-config/src/lib/mentoring-data-lists.ts b/libs/shared-config/src/lib/mentoring-data-lists.ts index 890ad264e..e18a8e654 100644 --- a/libs/shared-config/src/lib/mentoring-data-lists.ts +++ b/libs/shared-config/src/lib/mentoring-data-lists.ts @@ -10,341 +10,404 @@ export const MENTORING_GOALS = { export type MentoringGoalKey = keyof typeof MENTORING_GOALS -export const PROFESSIONAL_EXPERIENCE_FIELDS = { - humanResourcesAndRecruiting: 'Human resources and recruiting', +export const DESIRED_ROLES = { marketingSocialMediaAndSales: 'Marketing, social media and sales', - projectAndProductManagement: 'Project and product management', - requirementsAndBusinessAnalysis: 'Requirements and business analysis', + productAndProjectManagement: 'Product and project management', + requirementsAnalysisAndResearch: 'Requirements analysis and research', softwareDevelopment: 'Software development', - testingAndQa: 'Testing and QA', - usabilityAndUiDesign: 'Usability and UI design', + uxAndUiDesign: 'UX and UI design', + testingAndQualityAssurance: 'Testing and Quality Assurance', + other: 'Other', +} as const + +export const FIELDS_OF_EXPERTISE = { + humanResourcesAndRecruiting: 'Human resources and recruiting', + ...DESIRED_ROLES, } as const -export type ProfessionalExperienceFieldKey = - keyof typeof PROFESSIONAL_EXPERIENCE_FIELDS +export type FieldOfExperienceKey = keyof typeof FIELDS_OF_EXPERTISE export const MENTORING_TOPIC_GROUPS = { overarchingTopics: '๐ŸŒˆ Overarching topics', - productAndProjectManagement: '๐Ÿ“‹ Product and project management', - softwareDevelopment: '๐Ÿ‘ฉโ€๐Ÿ’ป Software development', requirementsAnalysisAndResearch: '๐Ÿงช Requirements analysis and research', uxAndUiDesign: '๐ŸŽจ UX and UI design', testingAndQualityAssurance: '๐ŸŽ๏ธ Testing and Quality Assurance', + productAndProjectManagement: '๐Ÿ“‹ Product and project management', + softwareDevelopment: '๐Ÿ‘ฉโ€๐Ÿ’ป Software development', toolsAndFrameworks: '๐Ÿ› ๏ธ Tools and frameworks', + toolsAndFrameworks2: '๐Ÿ› ๏ธ Tools and frameworks', } as const +/* +overarchingTopics +productAndProjectManagement +softwareDevelopment +*/ + export const MENTORING_TOPICS = [ { - id: 'applicationProcessAndPortfolio', + id: 'Application process and portfolio', label: 'Application process and portfolio', group: 'overarchingTopics', }, - { id: 'communication', label: 'Communication', group: 'overarchingTopics' }, - { - id: 'crossCulturalTeams', - label: 'Cross-cultural teams', - group: 'overarchingTopics', - }, + { id: 'Communication', label: 'Communication', group: 'overarchingTopics' }, + { id: 'Cross-cultural teams', label: 'Cross-cultural teams', group: '' }, { - id: 'crossFunctionalWork', + id: 'Cross-functional work', label: 'Cross-functional work', group: 'overarchingTopics', }, - { id: 'facilitation', label: 'Facilitation', group: 'overarchingTopics' }, + { id: 'Facilitation', label: 'Facilitation', group: 'overarchingTopics' }, { - id: 'givingReceivingFeedback', + id: 'Giving / receiving feedback', label: 'Giving / receiving feedback', group: 'overarchingTopics', }, { - id: 'marketingAndSocialMedia', - label: 'Marketing and social media', - group: 'overarchingTopics', - }, - { id: 'sales', label: 'Sales', group: 'overarchingTopics' }, - { - id: 'selfOrganisation', + id: 'Self-organisation', label: 'Self-organisation', group: 'overarchingTopics', }, { - id: 'socialNetworkProfileTuning', + id: 'Social network profile tuning', label: 'Social network profile tuning', group: 'overarchingTopics', }, { - id: 'storytellingAndPresentation', + id: 'Storytelling and presentation', label: 'Storytelling and presentation', group: 'overarchingTopics', }, { - id: 'teamLeadership', + id: 'Team leadership', label: 'Team leadership', group: 'overarchingTopics', }, { - id: 'agileFrameworks', + id: 'Time management', + label: 'Time management', + group: 'overarchingTopics', + }, + { + id: 'Marketing and social media', + label: 'Marketing and social media', + group: 'marketingSocialMediaAndSales', + }, + { id: 'Sales', label: 'Sales', group: 'marketingSocialMediaAndSales' }, + { + id: 'Data analysis', + label: 'Data analysis', + group: 'requirementsAnalysisAndResearch', + }, + { + id: 'Design research', + label: 'Design research', + group: 'requirementsAnalysisAndResearch', + }, + { + id: 'Design Thinking', + label: 'Design Thinking', + group: 'requirementsAnalysisAndResearch', + }, + { + id: 'Domain driven design', + label: 'Domain driven design', + group: 'requirementsAnalysisAndResearch', + }, + { + id: 'Mapping customer experience', + label: 'Mapping customer experience', + group: 'requirementsAnalysisAndResearch', + }, + { + id: 'Prioritisation metrics', + label: 'Prioritisation metrics', + group: 'requirementsAnalysisAndResearch', + }, + { + id: 'Process modelling', + label: 'Process modelling', + group: 'requirementsAnalysisAndResearch', + }, + { + id: 'Software Development Life Cycle', + label: 'Software Development Life Cycle', + group: 'requirementsAnalysisAndResearch', + }, + { + id: 'Technical writing', + label: 'Technical writing', + group: 'requirementsAnalysisAndResearch', + }, + { + id: 'User story mapping', + label: 'User story mapping', + group: 'requirementsAnalysisAndResearch', + }, + { + id: 'Qualitative User Research', + label: 'Qualitative User Research', + group: 'requirementsAnalysisAndResearch', + }, + { + id: 'Quantitative User Research', + label: 'Quantitative User Research', + group: 'requirementsAnalysisAndResearch', + }, + { id: 'Animation', label: 'Animation', group: 'uxAndUiDesign' }, + { id: 'Branding', label: 'Branding', group: 'uxAndUiDesign' }, + { + id: 'General user experience', + label: 'General user experience', + group: 'uxAndUiDesign', + }, + { id: 'Graphic Design', label: 'Graphic Design', group: 'uxAndUiDesign' }, + { + id: 'Information architecture', + label: 'Information architecture', + group: 'uxAndUiDesign', + }, + { + id: 'Interaction design', + label: 'Interaction design', + group: 'uxAndUiDesign', + }, + { + id: 'Prototyping and wireframing', + label: 'Prototyping and wireframing', + group: 'uxAndUiDesign', + }, + { + id: 'Responsive design', + label: 'Responsive design', + group: 'uxAndUiDesign', + }, + { + id: 'Typography / color theory', + label: 'Typography / color theory', + group: 'uxAndUiDesign', + }, + { + id: 'Visual communication', + label: 'Visual communication', + group: 'uxAndUiDesign', + }, + { + id: 'Automated software testing', + label: 'Automated software testing', + group: 'testingAndQualityAssurance', + }, + { + id: 'Behavior-driven development', + label: 'Behavior-driven development', + group: 'testingAndQualityAssurance', + }, + { + id: 'Cross-browser testing', + label: 'Cross-browser testing', + group: 'testingAndQualityAssurance', + }, + { + id: 'Functional Testing', + label: 'Functional Testing', + group: 'testingAndQualityAssurance', + }, + { + id: 'Software testing methodologies', + label: 'Software testing methodologies', + group: 'testingAndQualityAssurance', + }, + { + id: 'Test-driven development', + label: 'Test-driven development', + group: 'testingAndQualityAssurance', + }, + { + id: 'Test planning', + label: 'Test planning', + group: 'testingAndQualityAssurance', + }, + { + id: 'Test management', + label: 'Test management', + group: 'testingAndQualityAssurance', + }, + { + id: 'Usability testing', + label: 'Usability testing', + group: 'testingAndQualityAssurance', + }, + { + id: 'Agile frameworks', label: 'Agile frameworks', group: 'productAndProjectManagement', }, { - id: 'budgetCalculation', + id: 'Budget calculation', label: 'Budget calculation', group: 'productAndProjectManagement', }, { - id: 'businessDevelopment', + id: 'Business development', label: 'Business development', group: 'productAndProjectManagement', }, { - id: 'changeManagement', + id: 'Change management', label: 'Change management', group: 'productAndProjectManagement', }, { - id: 'enterpriseArchitectureManagement', + id: 'Enterprise architecture management', label: 'Enterprise architecture management', group: 'productAndProjectManagement', }, { - id: 'marketResearch', + id: 'Market research', label: 'Market research', group: 'productAndProjectManagement', }, { - id: 'planningAndRoadmaps', + id: 'Planning and roadmaps', label: 'Planning and roadmaps', group: 'productAndProjectManagement', }, { - id: 'prioritisationMetrics', + id: 'Prioritisation metrics', label: 'Prioritisation metrics', group: 'productAndProjectManagement', }, { - id: 'productBacklogManagement', + id: 'Product Backlog management', label: 'Product Backlog management', group: 'productAndProjectManagement', }, { - id: 'productManagement', + id: 'Product management', label: 'Product management', group: 'productAndProjectManagement', }, { - id: 'projectManagement', + id: 'Project management', label: 'Project management', group: 'productAndProjectManagement', }, { - id: 'androidMobileDevelopment', + id: 'Project management office', + label: 'Project management office', + group: 'productAndProjectManagement', + }, + { + id: 'Value-driven product development', + label: 'Value-driven product development', + group: 'productAndProjectManagement', + }, + { + id: 'Value metrics', + label: 'Value metrics', + group: 'productAndProjectManagement', + }, + { + id: 'Android Mobile Development', label: 'Android Mobile Development', group: 'softwareDevelopment', }, { - id: 'basicProgrammingSkills', + id: 'Basic programming skills', label: 'Basic programming skills', group: 'softwareDevelopment', }, { - id: 'computerNetworking', + id: 'Computer networking', label: 'Computer networking', group: 'softwareDevelopment', }, { - id: 'crossBrowserDevelopment', + id: 'Cross-browser development', label: 'Cross-browser development', group: 'softwareDevelopment', }, { - id: 'dataAnalytics', + id: 'Data analytics', label: 'Data analytics', group: 'softwareDevelopment', }, { - id: 'databaseManagement', + id: 'Database management', label: 'Database management', group: 'softwareDevelopment', }, { - id: 'devopsAndCloud', + id: 'DevOps and Cloud', label: 'DevOps and Cloud', group: 'softwareDevelopment', }, { - id: 'hybridAppDevelopment', + id: 'Hybrid App Development', label: 'Hybrid App Development', group: 'softwareDevelopment', }, { - id: 'hardwareAndNetworks', + id: 'Hardware and networks', label: 'Hardware and networks', group: 'softwareDevelopment', }, - { id: 'iot', label: 'IoT', group: 'softwareDevelopment' }, + { id: 'IoT', label: 'IoT', group: 'softwareDevelopment' }, { - id: 'iosMobileDevelopment', + id: 'iOS Mobile Development', label: 'iOS Mobile Development', group: 'softwareDevelopment', }, { - id: 'machineLearning', + id: 'Machine learning', label: 'Machine learning', group: 'softwareDevelopment', }, - - { - id: 'dataAnalysis', - label: 'Data analysis', - group: 'requirementsAndBusinessAnalysis', - }, - { - id: 'designResearch', - label: 'Design research', - group: 'requirementsAndBusinessAnalysis', - }, - { - id: 'designThinking', - label: 'Design Thinking', - group: 'requirementsAndBusinessAnalysis', - }, - { - id: 'domainDrivenDesign', - label: 'Domain driven design', - group: 'requirementsAndBusinessAnalysis', - }, - { - id: 'mappingCustomerExperience', - label: 'Mapping customer experience', - group: 'requirementsAndBusinessAnalysis', - }, - { - id: 'prioritisationMetrics', - label: 'Prioritisation metrics', - group: 'requirementsAndBusinessAnalysis', - }, - { - id: 'processModelling', - label: 'Process modelling', - group: 'requirementsAndBusinessAnalysis', - }, + { id: 'Security', label: 'Security', group: 'softwareDevelopment' }, { - id: 'softwareDevelopmentLifeCycle', - label: 'Software Development Life Cycle', - group: 'requirementsAndBusinessAnalysis', - }, - { - id: 'technicalWriting', - label: 'Technical writing', - group: 'requirementsAndBusinessAnalysis', - }, - { id: 'animation', label: 'Animation', group: 'uxAndUiDesign' }, - { id: 'branding', label: 'Branding', group: 'uxAndUiDesign' }, - { - id: 'generalUserExperience', - label: 'General user experience', - group: 'uxAndUiDesign', - }, - { id: 'graphicDesign', label: 'Graphic Design', group: 'uxAndUiDesign' }, - { - id: 'informationArchitecture', - label: 'Information architecture', - group: 'uxAndUiDesign', - }, - { - id: 'interactionDesign', - label: 'Interaction design', - group: 'uxAndUiDesign', - }, - { - id: 'prototypingAndWireframing', - label: 'Prototyping and wireframing', - group: 'uxAndUiDesign', - }, - { - id: 'responsiveDesign', - label: 'Responsive design', - group: 'uxAndUiDesign', - }, - { - id: 'typographyColorTheory', - label: 'Typography / color theory', - group: 'uxAndUiDesign', - }, - { - id: 'automatedSoftwareTesting', - label: 'Automated software testing', - group: 'testingAndQa', - }, - { - id: 'behaviorDrivenDevelopment', - label: 'Behavior-driven development', - group: 'testingAndQa', - }, - { - id: 'crossBrowserTesting', - label: 'Cross-browser testing', - group: 'testingAndQa', - }, - { - id: 'functionalTesting', - label: 'Functional Testing', - group: 'testingAndQa', - }, - { - id: 'softwareTestingMethodologies', - label: 'Software testing methodologies', - group: 'testingAndQa', - }, - { - id: 'testDrivenDevelopment', - label: 'Test-driven development', - group: 'testingAndQa', + id: 'Atlassian Jira', + label: 'Atlassian Jira', + group: 'toolsAndFrameworks', }, - { id: 'testPlanning', label: 'Test planning', group: 'testingAndQa' }, - { id: 'testManagement', label: 'Test management', group: 'testingAndQa' }, - { id: 'usabilityTesting', label: 'Usability testing', group: 'testingAndQa' }, - { id: 'atlassianJira', label: 'Atlassian Jira', group: 'toolsAndFrameworks' }, - { id: 'aws', label: 'AWS', group: 'toolsAndFrameworks' }, - { id: 'azure', label: 'Azure', group: 'toolsAndFrameworks' }, + { id: 'AWS', label: 'AWS', group: 'toolsAndFrameworks' }, + { id: 'Azure', label: 'Azure', group: 'toolsAndFrameworks' }, { - id: 'balsamiqMockup', + id: 'Balsamiq Mockup', label: 'Balsamiq Mockup', group: 'toolsAndFrameworks', }, - { id: 'blockchain', label: 'Blockchain', group: 'toolsAndFrameworks' }, - { id: 'cucumber', label: 'Cucumber', group: 'toolsAndFrameworks' }, - { id: 'cypress', label: 'Cypress', group: 'toolsAndFrameworks' }, - { id: 'docker', label: 'Docker', group: 'toolsAndFrameworks' }, - { id: 'figma', label: 'Figma', group: 'toolsAndFrameworks' }, - { id: 'flutter', label: 'Flutter', group: 'toolsAndFrameworks' }, - { id: 'gcp', label: 'GCP', group: 'toolsAndFrameworks' }, - { id: 'git', label: 'Git', group: 'toolsAndFrameworks' }, - { id: 'gherkin', label: 'Gherkin', group: 'toolsAndFrameworks' }, - { id: 'htmlCss', label: 'HTML & CSS', group: 'toolsAndFrameworks' }, - { id: 'java', label: 'Java', group: 'toolsAndFrameworks' }, - { id: 'javascript', label: 'JavaScript', group: 'toolsAndFrameworks' }, - { id: 'jest', label: 'Jest', group: 'toolsAndFrameworks' }, - { id: 'mongodb', label: 'MongoDB', group: 'toolsAndFrameworks' }, - { id: 'mysql', label: 'MySQL', group: 'toolsAndFrameworks' }, - { id: 'nodejs', label: 'NodeJS', group: 'toolsAndFrameworks' }, - { id: 'python', label: 'Python', group: 'toolsAndFrameworks' }, - { id: 'react', label: 'React', group: 'toolsAndFrameworks' }, - { id: 'reactNative', label: 'React Native', group: 'toolsAndFrameworks' }, - { id: 'restApis', label: "REST API's", group: 'toolsAndFrameworks' }, - { id: 'salesforce', label: 'Salesforce', group: 'toolsAndFrameworks' }, - { id: 'selenium', label: 'Selenium', group: 'toolsAndFrameworks' }, - { id: 'sketch', label: 'Sketch', group: 'toolsAndFrameworks' }, - { id: 'sql', label: 'SQL', group: 'toolsAndFrameworks' }, - { id: 'vmware', label: 'VMWare', group: 'toolsAndFrameworks' }, - { id: 'virtualBox', label: 'Virtual Box', group: 'toolsAndFrameworks' }, - { id: 'xray', label: 'Xray', group: 'toolsAndFrameworks' }, - { id: 'zephyr', label: 'Zephyr', group: 'toolsAndFrameworks' }, + { id: 'Blockchain', label: 'Blockchain', group: 'toolsAndFrameworks' }, + { id: 'Cucumber', label: 'Cucumber', group: 'toolsAndFrameworks' }, + { id: 'Cypress', label: 'Cypress', group: 'toolsAndFrameworks' }, + { id: 'Docker', label: 'Docker', group: 'toolsAndFrameworks' }, + { id: 'Figma', label: 'Figma', group: 'toolsAndFrameworks' }, + { id: 'Flutter', label: 'Flutter', group: 'toolsAndFrameworks' }, + { id: 'GCP', label: 'GCP', group: 'toolsAndFrameworks' }, + { id: 'Git', label: 'Git', group: 'toolsAndFrameworks' }, + { id: 'Gherkin', label: 'Gherkin', group: 'toolsAndFrameworks' }, + { id: 'HTML & CSS', label: 'HTML & CSS', group: 'toolsAndFrameworks' }, + { id: 'Java', label: 'Java', group: 'toolsAndFrameworks' }, + { id: 'JavaScript', label: 'JavaScript', group: 'toolsAndFrameworks' }, + { id: 'Jest', label: 'Jest', group: 'toolsAndFrameworks' }, + { id: 'MongoDB', label: 'MongoDB', group: 'toolsAndFrameworks' }, + { id: 'MySQL', label: 'MySQL', group: 'toolsAndFrameworks' }, + { id: 'NodeJS', label: 'NodeJS', group: 'toolsAndFrameworks' }, + { id: 'Python', label: 'Python', group: 'toolsAndFrameworks' }, + { id: 'React', label: 'React', group: 'toolsAndFrameworks' }, + { id: 'React Native', label: 'React Native', group: 'toolsAndFrameworks' }, + { id: "REST API's", label: "REST API's", group: 'toolsAndFrameworks' }, + { id: 'Salesforce', label: 'Salesforce', group: 'toolsAndFrameworks' }, + { id: 'Selenium', label: 'Selenium', group: 'toolsAndFrameworks' }, + { id: 'Sketch', label: 'Sketch', group: 'toolsAndFrameworks' }, + { id: 'SQL', label: 'SQL', group: 'toolsAndFrameworks' }, + { id: 'Xray', label: 'Xray', group: 'toolsAndFrameworks' }, + { id: 'Zephyr', label: 'Zephyr', group: 'toolsAndFrameworks' }, + { id: 'VMWare', label: 'VMWare', group: 'toolsAndFrameworks' }, + { id: 'Virtual Box', label: 'Virtual Box', group: 'toolsAndFrameworks' }, ] -export type MentoringTopicKey = typeof CATEGORIES[number]['id'] -export type MentoringTopicLabel = typeof CATEGORIES[number]['label'] +export type MentoringTopicKey = typeof MENTORING_TOPICS[number]['id'] +export type MentoringTopicLabel = typeof MENTORING_TOPICS[number]['label'] export const MENTORING_TOPICS_MAP = Object.fromEntries( MENTORING_TOPICS.map((topic) => [topic.id, topic.label]) diff --git a/libs/shared-types/src/lib/RedProfile.ts b/libs/shared-types/src/lib/RedProfile.ts index 13ed0ab2d..fbe6389df 100644 --- a/libs/shared-types/src/lib/RedProfile.ts +++ b/libs/shared-types/src/lib/RedProfile.ts @@ -10,8 +10,9 @@ import { GenderKey, Language, MenteeOccupationCategoryKey, + MentoringGoalKey, MentoringTopicKey, - ProfessionalExperienceFieldKey, + FieldOfExperienceKey, } from '@talent-connect/shared-config' export type RedProfile = { @@ -53,9 +54,17 @@ export type RedProfile = { favouritedRedProfileIds: Array optOutOfMenteesFromOtherRediLocation: boolean - mentoringTopics: Array - mentoringGoals: Array - professionalExperienceFields: Array + mentor_mentoringTopics: Array + mentor_mentoringGoals: Array + mentor_professionalExperienceFields: Array + + mentee_mentoringGoal: MentoringGoalKey + mentee_overarchingMentoringTopics: Array + mentee_primaryRole_fieldOfExpertise: FieldOfExperienceKey + mentee_primaryRole_mentoringTopics: Array + mentee_secondaryRole_fieldOfExpertise: FieldOfExperienceKey + mentee_secondaryRole_mentoringTopics: Array + mentee_toolsAndFrameworks_mentoringTopics: Array createdAt: Date updatedAt: Date From db4a4d18f479dc31acb29c1e96de05d882ec2af5 Mon Sep 17 00:00:00 2001 From: Eric Bolikowski Date: Tue, 14 Jun 2022 09:51:13 +0200 Subject: [PATCH 07/25] feat: restrict max topics count --- .../EditableMentoringGoalTopics.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoalTopics.tsx b/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoalTopics.tsx index f52e20e63..979ce51f7 100644 --- a/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoalTopics.tsx +++ b/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoalTopics.tsx @@ -41,6 +41,18 @@ const validationSchema = Yup.object({ mentee_primaryRole_fieldOfExpertise: Yup.string() .nullable() .required('Select a role'), + mentee_overarchingMentoringTopics: Yup.array().max( + 3, + 'You can select up to three topics' + ), + mentee_primaryRole_mentoringTopics: Yup.array().max( + 3, + 'You can select up to three skills' + ), + mentee_secondaryRole_mentoringTopics: Yup.array().max( + 3, + 'You can select up to three skills' + ), }) const EditableMentoringGoalTopics = ({ profile, profileSaveStart }: Props) => { From 8368234e918ab11c9d9a258a862eeb5c2421859d Mon Sep 17 00:00:00 2001 From: Eric Bolikowski Date: Sun, 26 Jun 2022 16:38:00 +0200 Subject: [PATCH 08/25] remove unused dummy cat --- libs/shared-config/src/lib/mentoring-data-lists.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/shared-config/src/lib/mentoring-data-lists.ts b/libs/shared-config/src/lib/mentoring-data-lists.ts index e18a8e654..ff8ea0dc3 100644 --- a/libs/shared-config/src/lib/mentoring-data-lists.ts +++ b/libs/shared-config/src/lib/mentoring-data-lists.ts @@ -35,7 +35,6 @@ export const MENTORING_TOPIC_GROUPS = { productAndProjectManagement: '๐Ÿ“‹ Product and project management', softwareDevelopment: '๐Ÿ‘ฉโ€๐Ÿ’ป Software development', toolsAndFrameworks: '๐Ÿ› ๏ธ Tools and frameworks', - toolsAndFrameworks2: '๐Ÿ› ๏ธ Tools and frameworks', } as const /* From 9da21a63cbd66d086b6bd8affe1ddb6ff2a13a0e Mon Sep 17 00:00:00 2001 From: Eric Bolikowski Date: Sun, 26 Jun 2022 17:13:22 +0200 Subject: [PATCH 09/25] reconfigure a couple mentoring topics --- libs/shared-config/src/lib/mentoring-data-lists.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libs/shared-config/src/lib/mentoring-data-lists.ts b/libs/shared-config/src/lib/mentoring-data-lists.ts index ff8ea0dc3..7a546d1f0 100644 --- a/libs/shared-config/src/lib/mentoring-data-lists.ts +++ b/libs/shared-config/src/lib/mentoring-data-lists.ts @@ -29,6 +29,7 @@ export type FieldOfExperienceKey = keyof typeof FIELDS_OF_EXPERTISE export const MENTORING_TOPIC_GROUPS = { overarchingTopics: '๐ŸŒˆ Overarching topics', + marketingSocialMediaAndSales: '๐Ÿšฆ Marketing, social media and sales', requirementsAnalysisAndResearch: '๐Ÿงช Requirements analysis and research', uxAndUiDesign: '๐ŸŽจ UX and UI design', testingAndQualityAssurance: '๐ŸŽ๏ธ Testing and Quality Assurance', @@ -50,7 +51,11 @@ export const MENTORING_TOPICS = [ group: 'overarchingTopics', }, { id: 'Communication', label: 'Communication', group: 'overarchingTopics' }, - { id: 'Cross-cultural teams', label: 'Cross-cultural teams', group: '' }, + { + id: 'Cross-cultural teams', + label: 'Cross-cultural teams', + group: 'overarchingTopics', + }, { id: 'Cross-functional work', label: 'Cross-functional work', From 0ab5f8b6278f5c4438de7d8d6cd0d48d1683a447 Mon Sep 17 00:00:00 2001 From: Eric Bolikowski Date: Mon, 1 Aug 2022 21:00:01 +0200 Subject: [PATCH 10/25] seed script generates value for new RedProfile props --- .gitignore | 1 + apps/api/scripts/seed-database.js | 452 +++++++++++++++++++++++++++++- 2 files changed, 452 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 44c040641..3525c32fe 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ Thumbs.db # mongodb data /mongodb-data/* !/mongodb-data/.gitkeep +/mongodb-data2/* # environment variables diff --git a/apps/api/scripts/seed-database.js b/apps/api/scripts/seed-database.js index c33f99a5f..0b6c0184c 100644 --- a/apps/api/scripts/seed-database.js +++ b/apps/api/scripts/seed-database.js @@ -24,6 +24,19 @@ const { RoleMapping, } = app.models +function pickRandomArrayElement(array) { + return array[Math.floor(Math.random() * array.length)] +} +function pickRandomArrayElements(array, maxCount) { + const count = maxCount + ? maxCount + : randomNumberInRange(1, Math.min(array.length)) + return _.sampleSize(array, count) +} +function randomNumberInRange(min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min +} + const personsRaw = require('./random-names.json') const persons = [] personsRaw.forEach((person) => { @@ -37,6 +50,402 @@ personsRaw.forEach((person) => { } }) +const MENTORING_GOALS = { + buildingAProfessionalNetwork: 'Building a professional network', + jobSearchAndApplicationProcess: 'Job search and application process', + entrepreneurshipAndFreelancing: 'Entrepreneurship and freelancing', + tutoringInAParticularSkillTool: 'Tutoring in a particular skill / tool', + careerOrientatioPlanning: 'Career orientation & planning', + preparationForACertificationInterview: + 'Preparation for a certification / interview', +} +const mentoringGoalsKeys = Object.keys(MENTORING_GOALS) + +const DESIRED_ROLES = { + marketingSocialMediaAndSales: 'Marketing, social media and sales', + productAndProjectManagement: 'Product and project management', + requirementsAnalysisAndResearch: 'Requirements analysis and research', + softwareDevelopment: 'Software development', + uxAndUiDesign: 'UX and UI design', + testingAndQualityAssurance: 'Testing and Quality Assurance', + other: 'Other', +} +const desiredRolesKeys = Object.keys(DESIRED_ROLES) + +const FIELDS_OF_EXPERTISE = { + humanResourcesAndRecruiting: 'Human resources and recruiting', + ...DESIRED_ROLES, +} +const fieldsOfExpertiseKeys = Object.keys(FIELDS_OF_EXPERTISE) + +const MENTORING_TOPICS = [ + { + id: 'Application process and portfolio', + label: 'Application process and portfolio', + group: 'overarchingTopics', + }, + { id: 'Communication', label: 'Communication', group: 'overarchingTopics' }, + { + id: 'Cross-cultural teams', + label: 'Cross-cultural teams', + group: 'overarchingTopics', + }, + { + id: 'Cross-functional work', + label: 'Cross-functional work', + group: 'overarchingTopics', + }, + { id: 'Facilitation', label: 'Facilitation', group: 'overarchingTopics' }, + { + id: 'Giving / receiving feedback', + label: 'Giving / receiving feedback', + group: 'overarchingTopics', + }, + { + id: 'Self-organisation', + label: 'Self-organisation', + group: 'overarchingTopics', + }, + { + id: 'Social network profile tuning', + label: 'Social network profile tuning', + group: 'overarchingTopics', + }, + { + id: 'Storytelling and presentation', + label: 'Storytelling and presentation', + group: 'overarchingTopics', + }, + { + id: 'Team leadership', + label: 'Team leadership', + group: 'overarchingTopics', + }, + { + id: 'Time management', + label: 'Time management', + group: 'overarchingTopics', + }, + { + id: 'Marketing and social media', + label: 'Marketing and social media', + group: 'marketingSocialMediaAndSales', + }, + { id: 'Sales', label: 'Sales', group: 'marketingSocialMediaAndSales' }, + { + id: 'Data analysis', + label: 'Data analysis', + group: 'requirementsAnalysisAndResearch', + }, + { + id: 'Design research', + label: 'Design research', + group: 'requirementsAnalysisAndResearch', + }, + { + id: 'Design Thinking', + label: 'Design Thinking', + group: 'requirementsAnalysisAndResearch', + }, + { + id: 'Domain driven design', + label: 'Domain driven design', + group: 'requirementsAnalysisAndResearch', + }, + { + id: 'Mapping customer experience', + label: 'Mapping customer experience', + group: 'requirementsAnalysisAndResearch', + }, + { + id: 'Prioritisation metrics', + label: 'Prioritisation metrics', + group: 'requirementsAnalysisAndResearch', + }, + { + id: 'Process modelling', + label: 'Process modelling', + group: 'requirementsAnalysisAndResearch', + }, + { + id: 'Software Development Life Cycle', + label: 'Software Development Life Cycle', + group: 'requirementsAnalysisAndResearch', + }, + { + id: 'Technical writing', + label: 'Technical writing', + group: 'requirementsAnalysisAndResearch', + }, + { + id: 'User story mapping', + label: 'User story mapping', + group: 'requirementsAnalysisAndResearch', + }, + { + id: 'Qualitative User Research', + label: 'Qualitative User Research', + group: 'requirementsAnalysisAndResearch', + }, + { + id: 'Quantitative User Research', + label: 'Quantitative User Research', + group: 'requirementsAnalysisAndResearch', + }, + { id: 'Animation', label: 'Animation', group: 'uxAndUiDesign' }, + { id: 'Branding', label: 'Branding', group: 'uxAndUiDesign' }, + { + id: 'General user experience', + label: 'General user experience', + group: 'uxAndUiDesign', + }, + { id: 'Graphic Design', label: 'Graphic Design', group: 'uxAndUiDesign' }, + { + id: 'Information architecture', + label: 'Information architecture', + group: 'uxAndUiDesign', + }, + { + id: 'Interaction design', + label: 'Interaction design', + group: 'uxAndUiDesign', + }, + { + id: 'Prototyping and wireframing', + label: 'Prototyping and wireframing', + group: 'uxAndUiDesign', + }, + { + id: 'Responsive design', + label: 'Responsive design', + group: 'uxAndUiDesign', + }, + { + id: 'Typography / color theory', + label: 'Typography / color theory', + group: 'uxAndUiDesign', + }, + { + id: 'Visual communication', + label: 'Visual communication', + group: 'uxAndUiDesign', + }, + { + id: 'Automated software testing', + label: 'Automated software testing', + group: 'testingAndQualityAssurance', + }, + { + id: 'Behavior-driven development', + label: 'Behavior-driven development', + group: 'testingAndQualityAssurance', + }, + { + id: 'Cross-browser testing', + label: 'Cross-browser testing', + group: 'testingAndQualityAssurance', + }, + { + id: 'Functional Testing', + label: 'Functional Testing', + group: 'testingAndQualityAssurance', + }, + { + id: 'Software testing methodologies', + label: 'Software testing methodologies', + group: 'testingAndQualityAssurance', + }, + { + id: 'Test-driven development', + label: 'Test-driven development', + group: 'testingAndQualityAssurance', + }, + { + id: 'Test planning', + label: 'Test planning', + group: 'testingAndQualityAssurance', + }, + { + id: 'Test management', + label: 'Test management', + group: 'testingAndQualityAssurance', + }, + { + id: 'Usability testing', + label: 'Usability testing', + group: 'testingAndQualityAssurance', + }, + { + id: 'Agile frameworks', + label: 'Agile frameworks', + group: 'productAndProjectManagement', + }, + { + id: 'Budget calculation', + label: 'Budget calculation', + group: 'productAndProjectManagement', + }, + { + id: 'Business development', + label: 'Business development', + group: 'productAndProjectManagement', + }, + { + id: 'Change management', + label: 'Change management', + group: 'productAndProjectManagement', + }, + { + id: 'Enterprise architecture management', + label: 'Enterprise architecture management', + group: 'productAndProjectManagement', + }, + { + id: 'Market research', + label: 'Market research', + group: 'productAndProjectManagement', + }, + { + id: 'Planning and roadmaps', + label: 'Planning and roadmaps', + group: 'productAndProjectManagement', + }, + { + id: 'Prioritisation metrics', + label: 'Prioritisation metrics', + group: 'productAndProjectManagement', + }, + { + id: 'Product Backlog management', + label: 'Product Backlog management', + group: 'productAndProjectManagement', + }, + { + id: 'Product management', + label: 'Product management', + group: 'productAndProjectManagement', + }, + { + id: 'Project management', + label: 'Project management', + group: 'productAndProjectManagement', + }, + { + id: 'Project management office', + label: 'Project management office', + group: 'productAndProjectManagement', + }, + { + id: 'Value-driven product development', + label: 'Value-driven product development', + group: 'productAndProjectManagement', + }, + { + id: 'Value metrics', + label: 'Value metrics', + group: 'productAndProjectManagement', + }, + { + id: 'Android Mobile Development', + label: 'Android Mobile Development', + group: 'softwareDevelopment', + }, + { + id: 'Basic programming skills', + label: 'Basic programming skills', + group: 'softwareDevelopment', + }, + { + id: 'Computer networking', + label: 'Computer networking', + group: 'softwareDevelopment', + }, + { + id: 'Cross-browser development', + label: 'Cross-browser development', + group: 'softwareDevelopment', + }, + { + id: 'Data analytics', + label: 'Data analytics', + group: 'softwareDevelopment', + }, + { + id: 'Database management', + label: 'Database management', + group: 'softwareDevelopment', + }, + { + id: 'DevOps and Cloud', + label: 'DevOps and Cloud', + group: 'softwareDevelopment', + }, + { + id: 'Hybrid App Development', + label: 'Hybrid App Development', + group: 'softwareDevelopment', + }, + { + id: 'Hardware and networks', + label: 'Hardware and networks', + group: 'softwareDevelopment', + }, + { id: 'IoT', label: 'IoT', group: 'softwareDevelopment' }, + { + id: 'iOS Mobile Development', + label: 'iOS Mobile Development', + group: 'softwareDevelopment', + }, + { + id: 'Machine learning', + label: 'Machine learning', + group: 'softwareDevelopment', + }, + { id: 'Security', label: 'Security', group: 'softwareDevelopment' }, + { + id: 'Atlassian Jira', + label: 'Atlassian Jira', + group: 'toolsAndFrameworks', + }, + { id: 'AWS', label: 'AWS', group: 'toolsAndFrameworks' }, + { id: 'Azure', label: 'Azure', group: 'toolsAndFrameworks' }, + { + id: 'Balsamiq Mockup', + label: 'Balsamiq Mockup', + group: 'toolsAndFrameworks', + }, + { id: 'Blockchain', label: 'Blockchain', group: 'toolsAndFrameworks' }, + { id: 'Cucumber', label: 'Cucumber', group: 'toolsAndFrameworks' }, + { id: 'Cypress', label: 'Cypress', group: 'toolsAndFrameworks' }, + { id: 'Docker', label: 'Docker', group: 'toolsAndFrameworks' }, + { id: 'Figma', label: 'Figma', group: 'toolsAndFrameworks' }, + { id: 'Flutter', label: 'Flutter', group: 'toolsAndFrameworks' }, + { id: 'GCP', label: 'GCP', group: 'toolsAndFrameworks' }, + { id: 'Git', label: 'Git', group: 'toolsAndFrameworks' }, + { id: 'Gherkin', label: 'Gherkin', group: 'toolsAndFrameworks' }, + { id: 'HTML & CSS', label: 'HTML & CSS', group: 'toolsAndFrameworks' }, + { id: 'Java', label: 'Java', group: 'toolsAndFrameworks' }, + { id: 'JavaScript', label: 'JavaScript', group: 'toolsAndFrameworks' }, + { id: 'Jest', label: 'Jest', group: 'toolsAndFrameworks' }, + { id: 'MongoDB', label: 'MongoDB', group: 'toolsAndFrameworks' }, + { id: 'MySQL', label: 'MySQL', group: 'toolsAndFrameworks' }, + { id: 'NodeJS', label: 'NodeJS', group: 'toolsAndFrameworks' }, + { id: 'Python', label: 'Python', group: 'toolsAndFrameworks' }, + { id: 'React', label: 'React', group: 'toolsAndFrameworks' }, + { id: 'React Native', label: 'React Native', group: 'toolsAndFrameworks' }, + { id: "REST API's", label: "REST API's", group: 'toolsAndFrameworks' }, + { id: 'Salesforce', label: 'Salesforce', group: 'toolsAndFrameworks' }, + { id: 'Selenium', label: 'Selenium', group: 'toolsAndFrameworks' }, + { id: 'Sketch', label: 'Sketch', group: 'toolsAndFrameworks' }, + { id: 'SQL', label: 'SQL', group: 'toolsAndFrameworks' }, + { id: 'Xray', label: 'Xray', group: 'toolsAndFrameworks' }, + { id: 'Zephyr', label: 'Zephyr', group: 'toolsAndFrameworks' }, + { id: 'VMWare', label: 'VMWare', group: 'toolsAndFrameworks' }, + { id: 'Virtual Box', label: 'Virtual Box', group: 'toolsAndFrameworks' }, +] +const mentoringTopicsKeys = MENTORING_TOPICS.map((topic) => topic.id) +const mentoringTopicsByDesiredRole = _.groupBy(MENTORING_TOPICS, 'group') + const categories = [ { id: 'basicProgrammingSkills', @@ -242,7 +651,7 @@ const users = fp.compose( Math.random() > 0.5 ? 'berlin' : Math.random() > 0.5 ? 'munich' : 'nrw' const email = randomString() + '@' + randomString() + '.com' const password = email - return { + const profile = { redUser: { email, password, @@ -292,6 +701,47 @@ const users = fp.compose( courses[Math.floor(Math.random() * courses.length)].id, }, } + if (profile.redProfile.userType.indexOf('mentee') !== -1) { + profile.redProfile.mentee_mentoringGoal = + pickRandomArrayElement(mentoringGoalsKeys) + profile.redProfile.mentee_overarchingMentoringTopics = + pickRandomArrayElements(mentoringGoalsKeys, 3) + profile.redProfile.mentee_primaryRole_fieldOfExpertise = + pickRandomArrayElement(desiredRolesKeys) + profile.redProfile.mentee_primaryRole_mentoringTopics = + pickRandomArrayElements( + mentoringTopicsByDesiredRole[ + profile.redProfile.mentee_primaryRole_fieldOfExpertise + ], + 3 + ) + profile.redProfile.mentee_secondaryRole_fieldOfExpertise = + pickRandomArrayElement(desiredRolesKeys) + profile.redProfile.mentee_secondaryRole_mentoringTopics = + pickRandomArrayElements( + mentoringTopicsByDesiredRole[ + profile.redProfile.mentee_secondaryRole_fieldOfExpertise + ], + 3 + ) + profile.redProfile.mentee_toolsAndFrameworks_mentoringTopics = + pickRandomArrayElements( + mentoringTopicsByDesiredRole['toolsAndFrameworks'], + 6 + ) + } else { + profile.redProfile.mentor_mentoringTopics = pickRandomArrayElements( + mentoringTopicsKeys, + 12 + ) + profile.redProfile.mentor_mentoringGoals = pickRandomArrayElements( + mentoringGoalsKeys, + 3 + ) + profile.redProfile.mentor_professionalExperienceFields = + pickRandomArrayElements(fieldsOfExpertiseKeys, 3) + } + return profile }) )(persons) From e20e58de36e1182bffc289cc3980d870037f7f2f Mon Sep 17 00:00:00 2001 From: Eric Bolikowski Date: Wed, 3 Aug 2022 16:28:17 +0200 Subject: [PATCH 11/25] update mentor listing --- apps/api/scripts/seed-database.js | 126 ++-- .../src/components/organisms/ProfileCard.tsx | 30 +- .../pages/app/find-a-mentor/FindAMentor.tsx | 548 ++++++++++++------ apps/redi-connect/src/services/api/api.tsx | 27 +- .../src/lib/mentoring-data-lists.ts | 2 + libs/shared-types/src/lib/RedProfile.ts | 2 +- 6 files changed, 491 insertions(+), 244 deletions(-) diff --git a/apps/api/scripts/seed-database.js b/apps/api/scripts/seed-database.js index 0b6c0184c..8e59efffa 100644 --- a/apps/api/scripts/seed-database.js +++ b/apps/api/scripts/seed-database.js @@ -11,6 +11,7 @@ const { tap, toArray, delay, + mergeMap, } = require('rxjs/operators') const moment = require('moment') @@ -68,7 +69,8 @@ const DESIRED_ROLES = { softwareDevelopment: 'Software development', uxAndUiDesign: 'UX and UI design', testingAndQualityAssurance: 'Testing and Quality Assurance', - other: 'Other', + // other: 'Other', // specifically commented out as there are no + // mentoring topics in the group 'other'. It causes issues when seeding } const desiredRolesKeys = Object.keys(DESIRED_ROLES) @@ -579,7 +581,7 @@ const categories = [ }, { id: 'entrepreneurship', label: 'Entrepreneurship', group: 'careerSupport' }, { id: 'freelancing', label: 'Freelancing', group: 'careerSupport' }, -] +].map((p) => p.id) const Languages = ['German', 'Arabic', 'Farsi', 'Tigrinya'] @@ -644,6 +646,67 @@ const pickRandomUserType = () => { return possibleUserTypes[randomIndex] } +const addMentorSpecificProperties = (profile) => { + profile.mentor_mentoringTopics = pickRandomArrayElements( + mentoringTopicsKeys, + 6 + ) + profile.mentor_mentoringGoals = pickRandomArrayElements(mentoringGoalsKeys, 2) + profile.mentor_professionalExperienceFields = pickRandomArrayElements( + fieldsOfExpertiseKeys, + 2 + ) + return profile +} + +const addMenteeSpecificProperties = (profile) => { + profile.mentee_mentoringGoal = pickRandomArrayElement(mentoringGoalsKeys) + profile.mentee_overarchingMentoringTopics = pickRandomArrayElements( + mentoringTopicsByDesiredRole['overarchingTopics'].map((p) => p.id), + 3 + ) + profile.mentee_primaryRole_fieldOfExpertise = + pickRandomArrayElement(desiredRolesKeys) + if ( + !mentoringTopicsByDesiredRole[profile.mentee_primaryRole_fieldOfExpertise] + ) { + console.log(profile.mentee_primaryRole_fieldOfExpertise) + console.log( + mentoringTopicsByDesiredRole[profile.mentee_primaryRole_fieldOfExpertise] + ) + } + profile.mentee_primaryRole_mentoringTopics = pickRandomArrayElements( + mentoringTopicsByDesiredRole[ + profile.mentee_primaryRole_fieldOfExpertise + ].map((p) => p.id), + 3 + ) + profile.mentee_secondaryRole_fieldOfExpertise = + pickRandomArrayElement(desiredRolesKeys) + if ( + !mentoringTopicsByDesiredRole[profile.mentee_secondaryRole_fieldOfExpertise] + ) { + console.log(profile.mentee_secondaryRole_fieldOfExpertise) + console.log( + mentoringTopicsByDesiredRole[ + profile.mentee_secondaryRole_fieldOfExpertise + ] + ) + } + profile.mentee_secondaryRole_mentoringTopics = pickRandomArrayElements( + mentoringTopicsByDesiredRole[ + profile.mentee_secondaryRole_fieldOfExpertise + ].map((p) => p.id), + 3 + ) + profile.mentee_toolsAndFrameworks_mentoringTopics = pickRandomArrayElements( + mentoringTopicsByDesiredRole['toolsAndFrameworks'].map((p) => p.id), + 6 + ) + + return profile +} + const users = fp.compose( fp.take(1000), fp.map(({ name, surname, gender }) => { @@ -681,8 +744,8 @@ const users = fp.compose( mentee_occupationOther_description: randomString(), profileAvatarImageS3Key: 'c1774822-9495-4bd6-866a-bf4d28aaddc8_ScreenShot2019-03-12at22.22.20.png', - languages: Languages.filter(() => Math.random() > 0.5).concat( - 'English' + languages: ['English'].concat( + Languages.filter(() => Math.random() > 0.5) ), otherLanguages: randomString(), personalDescription: randomString(undefined, 300), @@ -702,44 +765,9 @@ const users = fp.compose( }, } if (profile.redProfile.userType.indexOf('mentee') !== -1) { - profile.redProfile.mentee_mentoringGoal = - pickRandomArrayElement(mentoringGoalsKeys) - profile.redProfile.mentee_overarchingMentoringTopics = - pickRandomArrayElements(mentoringGoalsKeys, 3) - profile.redProfile.mentee_primaryRole_fieldOfExpertise = - pickRandomArrayElement(desiredRolesKeys) - profile.redProfile.mentee_primaryRole_mentoringTopics = - pickRandomArrayElements( - mentoringTopicsByDesiredRole[ - profile.redProfile.mentee_primaryRole_fieldOfExpertise - ], - 3 - ) - profile.redProfile.mentee_secondaryRole_fieldOfExpertise = - pickRandomArrayElement(desiredRolesKeys) - profile.redProfile.mentee_secondaryRole_mentoringTopics = - pickRandomArrayElements( - mentoringTopicsByDesiredRole[ - profile.redProfile.mentee_secondaryRole_fieldOfExpertise - ], - 3 - ) - profile.redProfile.mentee_toolsAndFrameworks_mentoringTopics = - pickRandomArrayElements( - mentoringTopicsByDesiredRole['toolsAndFrameworks'], - 6 - ) + addMenteeSpecificProperties(profile.redProfile) } else { - profile.redProfile.mentor_mentoringTopics = pickRandomArrayElements( - mentoringTopicsKeys, - 12 - ) - profile.redProfile.mentor_mentoringGoals = pickRandomArrayElements( - mentoringGoalsKeys, - 3 - ) - profile.redProfile.mentor_professionalExperienceFields = - pickRandomArrayElements(fieldsOfExpertiseKeys, 3) + addMentorSpecificProperties(profile.redProfile) } return profile }) @@ -813,6 +841,7 @@ const ericMenteeRedProfile = { mentee_currentlyEnrolledInCourse: 'salesforceFundamentals', username: 'career+testmentee@redi-school.org', } +addMenteeSpecificProperties(ericMenteeRedProfile) const ericMentorRedUser = { password: 'career+testmentor@redi-school.org', @@ -854,6 +883,7 @@ const ericMentorRedProfile = { menteeCountCapacity: 2, username: 'career+testmentor@redi-school.org', } +addMentorSpecificProperties(ericMentorRedProfile) const ericAdminUser = { email: 'cloud-accounts@redi-school.org', @@ -892,7 +922,7 @@ const ericAdminRedProfile = { githubProfileUrl: '', telephoneNumber: '', categories: categories.map((c) => c.id).filter(() => Math.random() < 0.4), - menteeCountCapacity: 2, + menteeCountCapacity: 0, username: 'cloud-accounts@redi-school.org', } @@ -905,7 +935,7 @@ Rx.of({}) switchMap(redUserDestroyAll), switchMap(redProfileDestroyAll), tap(() => console.log('destroyed')), - delay(10000), + delay(2000), // switchMap(redMentoringSessionDestroyAll), switchMap(() => redUserCreate(ericAdminUser)), switchMap((redUser) => @@ -921,14 +951,16 @@ Rx.of({}) redProfileCreateOnRedUser(redUser)(ericMentorRedProfile) ), switchMapTo(users), - concatMap( + mergeMap( (userData) => redUserCreate(userData.redUser), - (userData, redUserInst) => ({ ...userData, redUserInst }) + (userData, redUserInst) => ({ ...userData, redUserInst }), + 10 ), - concatMap( + mergeMap( (userData) => redProfileCreateOnRedUser(userData.redUserInst)(userData.redProfile), - (userData, redProfileInst) => ({ ...userData, redProfileInst }) + (userData, redProfileInst) => ({ ...userData, redProfileInst }), + 10 ), toArray(), // Pick X mentor-mentee pairs, create match diff --git a/apps/redi-connect/src/components/organisms/ProfileCard.tsx b/apps/redi-connect/src/components/organisms/ProfileCard.tsx index b615b6788..869d70055 100644 --- a/apps/redi-connect/src/components/organisms/ProfileCard.tsx +++ b/apps/redi-connect/src/components/organisms/ProfileCard.tsx @@ -7,6 +7,8 @@ import { useHistory } from 'react-router-dom' import { AWS_PROFILE_AVATARS_BUCKET_BASE_URL, REDI_LOCATION_NAMES, + FIELDS_OF_EXPERTISE, + MENTORING_TOPICS_MAP, } from '@talent-connect/shared-config' import placeholderImage from '../../assets/images/img-placeholder.png' @@ -30,15 +32,23 @@ const ProfileCard = ({ }: ProfileCardProps) => { const history = useHistory() - const { + let { firstName, lastName, languages, - categories, + mentor_mentoringTopics, + mentor_professionalExperienceFields, rediLocation, profileAvatarImageS3Key, } = profile + const mentoringTopics = + mentor_mentoringTopics?.map((topic) => MENTORING_TOPICS_MAP[topic]) ?? [] + const professionalExperienceFields = + mentor_professionalExperienceFields?.map( + (field) => FIELDS_OF_EXPERTISE[field] + ) ?? [] + const handleFavorite = (e: React.MouseEvent) => { e.stopPropagation() toggleFavorite && toggleFavorite(profile.id) @@ -82,9 +92,19 @@ const ProfileCard = ({ {REDI_LOCATION_NAMES[rediLocation]} {languages && } - {categories && ( - - )} +

+ + Mentors in these overarching topics, role-related skills and + tools/frameworks: + + {mentoringTopics?.length > 0 ? mentoringTopics.join(' | ') : null} +

+

+ Has professional experience in these fields: + {professionalExperienceFields?.length > 0 + ? professionalExperienceFields.join(' | ') + : null} +

) diff --git a/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx b/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx index 33480800c..2ffe127c8 100644 --- a/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx +++ b/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useState } from 'react' import { Content, Columns, Tag, Form } from 'react-bulma-components' -import { debounce } from 'lodash' +import { debounce, values } from 'lodash' import { Heading, Icon, @@ -16,7 +16,18 @@ import { connect } from 'react-redux' import { RootState } from '../../../redux/types' import { LoggedIn } from '../../../components/templates' -import { CATEGORIES, REDI_LOCATION_NAMES } from '@talent-connect/shared-config' +import { + CATEGORIES, + Language, + MentoringGoalKey, + DesiredRolesKey, + MentoringTopicKey, + REDI_LOCATION_NAMES, + MENTORING_TOPICS, + MENTORING_TOPICS_MAP, + FIELDS_OF_EXPERTISE, + FieldOfExperienceKey, +} from '@talent-connect/shared-config' import './FindAMentor.scss' import { toggleValueInArray } from './utils' import { @@ -28,63 +39,46 @@ import { } from 'use-query-params' import { objectKeys } from '@talent-connect/typescript-utilities' -const filterCategories = CATEGORIES.map((category) => ({ - value: category.id, - label: category.label, -})) - -interface FilterTagProps { - id: string - label: string - onClickHandler: (item: string) => void -} - -const FilterTag = ({ id, label, onClickHandler }: FilterTagProps) => ( - - {label} - { - onClickHandler(id) - }} - className="active-filters__remove" - /> - -) - interface FindAMentorProps { profile: RedProfile profileSaveStart: (profile: Partial) => void } -const FindAMentor = ({ profile, profileSaveStart }: FindAMentorProps) => { +function FindAMentor({ profile, profileSaveStart }: FindAMentorProps) { const { Loading, isLoading, setLoading } = useLoading() - const { - id, - categories: categoriesFromProfile, - favouritedRedProfileIds, - rediLocation, - } = profile - const [showFavorites, setShowFavorites] = useState(false) - const [mentors, setMentors] = useState([]) - const [query, setQuery] = useQueryParams({ - name: withDefault(StringParam, undefined), - topics: withDefault(ArrayParam, []), - languages: withDefault(ArrayParam, []), - locations: withDefault(ArrayParam, []), + const [allFetchedMentors, setAllFetchedMentors] = useState([]) + const [ + { + nameQuery, + onlyFavorites, + mentoringTopics, + mentoringTopicsToolsAndFrameworks, + locations, + roles, + }, + setQuery, + ] = useQueryParams({ + nameQuery: withDefault(StringParam, undefined), onlyFavorites: withDefault(BooleanParam, undefined), + mentoringTopics: withDefault(ArrayParam, []), + mentoringTopicsToolsAndFrameworks: withDefault(ArrayParam, []), + locations: withDefault(ArrayParam, []), + roles: withDefault(ArrayParam, []), }) - const { topics, name, languages, locations, onlyFavorites } = query useEffect(() => { - const hasQuery = - topics.length > 0 || - languages.length > 0 || - locations.length > 0 || - Boolean(name) || - onlyFavorites - setQuery(hasQuery ? query : { ...query, topics: categoriesFromProfile }) - }, []) + setLoading(true) + getMentors({ + nameQuery: nameQuery ?? '', + }).then((mentors) => { + console.log(mentors) + setAllFetchedMentors(mentors) + setLoading(false) + }) + }, [nameQuery]) + + const { id, favouritedRedProfileIds, rediLocation } = profile + const [showFavorites, setShowFavorites] = useState(false) const toggleFilters = (filtersArr, filterName, item) => { const newFilters = toggleValueInArray(filtersArr, item) @@ -92,7 +86,10 @@ const FindAMentor = ({ profile, profileSaveStart }: FindAMentorProps) => { } const setName = (value) => { - setQuery((latestQuery) => ({ ...latestQuery, name: value || undefined })) + setQuery((latestQuery) => ({ + ...latestQuery, + nameQuery: value || undefined, + })) } const toggleFavorites = (favoritesArr, value) => { @@ -113,141 +110,170 @@ const FindAMentor = ({ profile, profileSaveStart }: FindAMentorProps) => { const clearFilters = () => { setQuery((latestQuery) => ({ ...latestQuery, - topics: [], - languages: [], + mentoringTopics: [], + mentoringTopicsToolsAndFrameworks: [], locations: [], + roles: [], })) } - const filterLanguages = Array.from( - new Set( - mentors - .map((mentor) => mentor.languages || []) - .flat() - .sort() + if (profile.userActivated !== true) return + + const eligibleMentors = allFetchedMentors + .filter((mentor) => mentor.currentFreeMenteeSpots > 0) + .filter( + (mentor) => + !mentor.optOutOfMenteesFromOtherRediLocation || + mentor.rediLocation === rediLocation ) - ).map((language) => ({ - value: language, - label: language, - })) - - const filterRediLocations = objectKeys(REDI_LOCATION_NAMES).map( - (location) => ({ - value: location, - label: REDI_LOCATION_NAMES[location as RediLocation] as string, - }) - ) - useEffect(() => { - setLoading(true) - getMentors({ - categories: topics, - languages, - nameQuery: name || '', - locations, - }).then((mentors) => { - setMentors( - mentors - .filter((mentor) => mentor.currentFreeMenteeSpots > 0) - .filter( - (mentor) => - !mentor.optOutOfMenteesFromOtherRediLocation || - mentor.rediLocation === rediLocation - ) - ) - setLoading(false) - }) - }, [topics, languages, locations, name]) + const mentorGroups = filterGroupRankMentors(eligibleMentors, { + menteeDesiredRoles: [ + profile.mentee_primaryRole_fieldOfExpertise, + profile.mentee_secondaryRole_fieldOfExpertise, + ], + menteeLanguages: profile.languages, + menteeMentoringGoal: profile.mentee_mentoringGoal, + menteePrimaryRoleMentoringTopics: + profile.mentee_primaryRole_mentoringTopics, + menteeSecondaryRoleMentoringTopics: + profile.mentee_secondaryRole_mentoringTopics, + menteeToolsAndFrameworksTopics: + profile.mentee_toolsAndFrameworks_mentoringTopics, - if (profile.userActivated !== true) return + chosenMentoringTopics: mentoringTopics, + chosenToolsAndFrameworks: mentoringTopicsToolsAndFrameworks, + chosenLocations: locations as Array, + chosedDesiredRoles: roles as Array, + + favoritedMentorsEnabled: showFavorites, + favoritedMentors: favouritedRedProfileIds, + }) + + const mentorCount = + mentorGroups.bestMatchMentors.length + mentorGroups.otherMentors.length return ( - Available mentors ({mentors.length}) + Available mentors ({mentorCount})
- +
+ +
+ +
+ + Only Favorites +
toggleFilters(topics, 'topics', item)} + selected={mentoringTopics} + onChange={(item) => + toggleFilters(mentoringTopics, 'mentoringTopics', item) + } />
-
+
toggleFilters(languages, 'languages', item)} + label="Tools and Frameworks" + selected={mentoringTopicsToolsAndFrameworks} + onChange={(item) => + toggleFilters( + mentoringTopicsToolsAndFrameworks, + 'mentoringTopicsToolsAndFrameworks', + item + ) + } /> -
- - Only Favorites -
-
-
-
+
toggleFilters(locations, 'locations', item)} />
- - {(topics.length !== 0 || - languages.length !== 0 || - locations.length !== 0) && ( +
+
+
+ toggleFilters(roles, 'roles', item)} + /> +
+
+
+ {(mentoringTopics.length !== 0 || + mentoringTopicsToolsAndFrameworks.length !== 0 || + locations.length !== 0 || + roles.length !== 0) && ( <> - {(topics as string[]).map((catId) => ( + {(mentoringTopics as string[]).map((id) => ( item.id === catId).label} - onClickHandler={(item) => toggleFilters(topics, 'topics', item)} + key={id} + id={id} + label={MENTORING_TOPICS_MAP[id]} + onClickHandler={(item) => + toggleFilters(mentoringTopics, 'mentoringTopics', item) + } /> ))} - {(languages as string[]).map((langId) => ( + {(mentoringTopicsToolsAndFrameworks as string[]).map((id) => ( - toggleFilters(languages, 'languages', item) + toggleFilters( + mentoringTopicsToolsAndFrameworks, + 'mentoringTopicsToolsAndFrameworks', + item + ) } /> ))} - {(locations as RediLocation[]).map( - (locId) => - locId && ( - - toggleFilters(locations, 'locations', item) - } - /> - ) - )} + {(locations as string[]).map((id) => ( + + toggleFilters(locations, 'locations', item) + } + /> + ))} + {(roles as string[]).map((id) => ( + toggleFilters(roles, 'roles', item)} + /> + ))} + Delete all filters @@ -256,28 +282,7 @@ const FindAMentor = ({ profile, profileSaveStart }: FindAMentorProps) => { )}
- - {mentors.map((mentor: RedProfile) => { - const isFavorite = favouritedRedProfileIds.includes(mentor.id) - - if (!isFavorite && showFavorites) return - - return ( - - - toggleFavorites(favouritedRedProfileIds, item) - } - isFavorite={isFavorite} - /> - - ) - })} - - - {mentors.length === 0 && !isLoading && ( + {mentorCount === 0 && !isLoading && ( <> Unfortunately could not find any mentors matching @@ -285,6 +290,40 @@ const FindAMentor = ({ profile, profileSaveStart }: FindAMentorProps) => { )} + {mentorCount > 0 && ( + <> +

Best Matches

+ + {mentorGroups.bestMatchMentors.map((mentor: RedProfile) => ( + + + toggleFavorites(favouritedRedProfileIds, item) + } + isFavorite={favouritedRedProfileIds.includes(mentor.id)} + /> + + ))} + +

All mentors

+ + {mentorGroups.otherMentors.map((mentor: RedProfile) => ( + + + toggleFavorites(favouritedRedProfileIds, item) + } + isFavorite={favouritedRedProfileIds.includes(mentor.id)} + /> + + ))} + + + )} ) } @@ -297,3 +336,182 @@ const mapDispatchToProps = (dispatch: any) => ({ dispatch(profileSaveStart(profile)), }) export default connect(mapStateToProps, mapDispatchToProps)(FindAMentor) + +const filterItemsMentoringTopics = MENTORING_TOPICS.filter( + ({ group }) => group !== 'toolsAndFrameworks' +).map((category) => ({ + value: category.id, + label: category.label, +})) +const filterItemsToolsAndFrameworks = MENTORING_TOPICS.filter( + ({ group }) => group === 'toolsAndFrameworks' +).map((category) => ({ + value: category.id, + label: category.label, +})) +const filterItemsLocations = Object.entries(REDI_LOCATION_NAMES).map( + ([value, label]) => ({ value, label }) +) +const filterItemsRoles = Object.entries(FIELDS_OF_EXPERTISE).map( + ([value, label]) => ({ value, label }) +) + +interface FilterTagProps { + id: string + label: string + onClickHandler: (item: string) => void +} + +const FilterTag = ({ id, label, onClickHandler }: FilterTagProps) => ( + + {label} + { + onClickHandler(id) + }} + className="active-filters__remove" + /> + +) + +interface FilteredMentors { + bestMatchMentors: RedProfile[] + otherMentors: RedProfile[] +} +interface FiltersValues { + menteeLanguages: Array + menteeMentoringGoal: MentoringGoalKey + menteeDesiredRoles: Array + menteePrimaryRoleMentoringTopics: Array + menteeSecondaryRoleMentoringTopics: Array + menteeToolsAndFrameworksTopics: Array + + chosenMentoringTopics: Array + chosenToolsAndFrameworks: Array + chosenLocations: Array + chosedDesiredRoles: Array + + favoritedMentorsEnabled: boolean + favoritedMentors: string[] +} + +const filterGroupRankMentors = ( + mentors: RedProfile[], + filters: FiltersValues +): FilteredMentors => { + const filterFns = curriedFilterFunctions(filters) + const displayableMentors = mentors + .filter(filterFns.favorites) + .filter(filterFns.chosenMentoringTopics) + .filter(filterFns.chosenToolsAndFrameworks) + .filter(filterFns.chosenLocations) + .filter(filterFns.chosenDesiredRoles) + .filter(filterFns.mentorSharesLanguageWithMentee) + .filter(filterFns.menteeMentoringGoalCompatibleWithMentor) + + const groupedMentors = { + bestMatchMentors: [] as Array, + otherMentors: [] as Array, + } + + for (const mentor of displayableMentors) { + if (filterFns.isBestMatchMentor(mentor)) + groupedMentors.bestMatchMentors.push(mentor) + else groupedMentors.otherMentors.push(mentor) + } + + return groupedMentors +} +const curriedFilterFunctions = (filters: FiltersValues) => { + const allMenteeRoleMentoringTopics = [ + ...filters.menteePrimaryRoleMentoringTopics, + ...filters.menteeSecondaryRoleMentoringTopics, + ] + + return { + favorites(mentor: RedProfile) { + if (!filters.favoritedMentorsEnabled) return true + return filters.favoritedMentors.includes(mentor.id) + }, + chosenMentoringTopics(mentor: RedProfile) { + if (filters.chosenMentoringTopics.length === 0) return true + return mentor.mentor_mentoringTopics.some((topic) => + filters.chosenMentoringTopics.includes(topic) + ) + }, + chosenToolsAndFrameworks(mentor: RedProfile) { + if (filters.chosenToolsAndFrameworks.length === 0) return true + return mentor.mentor_mentoringTopics.some((topic) => + filters.chosenToolsAndFrameworks.includes(topic) + ) + }, + chosenLocations(mentor: RedProfile) { + if (filters.chosenLocations.length === 0) return true + return filters.chosenLocations.includes(mentor.rediLocation) + }, + chosenDesiredRoles(mentor: RedProfile) { + if (filters.chosedDesiredRoles.length === 0) return true + return mentor.mentor_professionalExperienceFields.some((field) => + filters.chosedDesiredRoles.includes(field) + ) + }, + mentorSharesLanguageWithMentee(mentor: RedProfile) { + return mentor.languages.some((lang) => + filters.menteeLanguages.includes(lang) + ) + }, + menteeMentoringGoalCompatibleWithMentor(mentor: RedProfile) { + switch (filters.menteeMentoringGoal) { + case 'tutoringInAParticularSkillTool': + case 'preparationForACertificationInterview': + case 'careerOrientatioPlanning': + if ( + !mentor.mentor_professionalExperienceFields.some((field) => + filters.menteeDesiredRoles.includes(field) + ) + ) + return false + + case 'tutoringInAParticularSkillTool': + case 'preparationForACertificationInterview': + if ( + !allMenteeRoleMentoringTopics.some((topic) => + mentor.mentor_mentoringTopics.includes(topic) + ) + ) + return false + + default: + return true + } + }, + isBestMatchMentor(mentor: RedProfile) { + switch (filters.menteeMentoringGoal) { + case 'tutoringInAParticularSkillTool': + case 'preparationForACertificationInterview': + return filters.menteeToolsAndFrameworksTopics.some((topic) => + mentor.mentor_mentoringTopics.includes(topic) + ) + + case 'careerOrientatioPlanning': + return allMenteeRoleMentoringTopics.some((topic) => + mentor.mentor_mentoringTopics.includes(topic) + ) + + case 'jobSearchAndApplicationProcess': + case 'buildingAProfessionalNetwork': + case 'entrepreneurshipAndFreelancing': + return allMenteeRoleMentoringTopics.some((topic) => + mentor.mentor_mentoringTopics.includes(topic) + ) + + default: + // Exhaustiveness check: we should never hit this clause + throw new Error( + `Unexpected mentee mentoring goal: ${filters.menteeMentoringGoal}` + ) + } + }, + } +} diff --git a/apps/redi-connect/src/services/api/api.tsx b/apps/redi-connect/src/services/api/api.tsx index 48853f9e1..93ddc6894 100644 --- a/apps/redi-connect/src/services/api/api.tsx +++ b/apps/redi-connect/src/services/api/api.tsx @@ -129,25 +129,11 @@ export interface RedProfileFilters { export const getProfiles = ({ userType, - categories, - languages, - locations, nameQuery, }: RedProfileFilters): Promise => { - const filterLanguages = - languages && languages.length !== 0 ? { inq: languages } : undefined - const filterCategories = - categories && categories.length !== 0 ? { inq: categories } : undefined - const filterLocations = - locations && locations.length !== 0 ? { inq: locations } : undefined - return http( `${API_URL}/redProfiles?filter=${JSON.stringify({ where: { - // loopbackComputedDoNotSetElsewhere__forAdminSearch__fullName: { - // like: 'Carlotta3', - // options: 'i', - // }, and: [ ...String(nameQuery) .split(' ') @@ -158,9 +144,6 @@ export const getProfiles = ({ }, })), { userType }, - { languages: filterLanguages }, - { categories: filterCategories }, - { rediLocation: filterLocations }, { userActivated: true }, ], }, @@ -170,17 +153,9 @@ export const getProfiles = ({ ).then((resp) => resp.data) } -export const getMentors = ({ - categories, - languages, - locations, - nameQuery, -}: Partial) => +export const getMentors = ({ nameQuery }: Partial) => getProfiles({ userType: 'mentor', - categories, - languages, - locations, nameQuery, }) diff --git a/libs/shared-config/src/lib/mentoring-data-lists.ts b/libs/shared-config/src/lib/mentoring-data-lists.ts index 7a546d1f0..007180a03 100644 --- a/libs/shared-config/src/lib/mentoring-data-lists.ts +++ b/libs/shared-config/src/lib/mentoring-data-lists.ts @@ -20,6 +20,8 @@ export const DESIRED_ROLES = { other: 'Other', } as const +export type DesiredRolesKey = keyof typeof FIELDS_OF_EXPERTISE + export const FIELDS_OF_EXPERTISE = { humanResourcesAndRecruiting: 'Human resources and recruiting', ...DESIRED_ROLES, diff --git a/libs/shared-types/src/lib/RedProfile.ts b/libs/shared-types/src/lib/RedProfile.ts index fbe6389df..ed2737ac1 100644 --- a/libs/shared-types/src/lib/RedProfile.ts +++ b/libs/shared-types/src/lib/RedProfile.ts @@ -55,7 +55,7 @@ export type RedProfile = { optOutOfMenteesFromOtherRediLocation: boolean mentor_mentoringTopics: Array - mentor_mentoringGoals: Array + mentor_mentoringGoals: Array mentor_professionalExperienceFields: Array mentee_mentoringGoal: MentoringGoalKey From 3cd9333ce811678b31aeaa28ba38ba94b656d4c1 Mon Sep 17 00:00:00 2001 From: Eric Bolikowski Date: Wed, 3 Aug 2022 16:36:20 +0200 Subject: [PATCH 12/25] display group count --- .../src/pages/app/find-a-mentor/FindAMentor.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx b/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx index 2ffe127c8..bae0883f4 100644 --- a/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx +++ b/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx @@ -292,7 +292,9 @@ function FindAMentor({ profile, profileSaveStart }: FindAMentorProps) { )} {mentorCount > 0 && ( <> -

Best Matches

+

+ Best Matches ({mentorGroups.bestMatchMentors.length}) +

{mentorGroups.bestMatchMentors.map((mentor: RedProfile) => ( @@ -307,7 +309,9 @@ function FindAMentor({ profile, profileSaveStart }: FindAMentorProps) { ))} -

All mentors

+

+ All mentors ({mentorGroups.otherMentors.length}) +

{mentorGroups.otherMentors.map((mentor: RedProfile) => ( @@ -423,6 +427,7 @@ const filterGroupRankMentors = ( return groupedMentors } + const curriedFilterFunctions = (filters: FiltersValues) => { const allMenteeRoleMentoringTopics = [ ...filters.menteePrimaryRoleMentoringTopics, From d2d119402bb552dd23accbe92fce50074435b223 Mon Sep 17 00:00:00 2001 From: kate Date: Thu, 3 Nov 2022 22:04:31 +0100 Subject: [PATCH 13/25] Fix undefined values --- .../src/pages/app/find-a-mentor/FindAMentor.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx b/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx index bae0883f4..8f3243248 100644 --- a/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx +++ b/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx @@ -321,7 +321,7 @@ function FindAMentor({ profile, profileSaveStart }: FindAMentorProps) { toggleFavorite={(item) => toggleFavorites(favouritedRedProfileIds, item) } - isFavorite={favouritedRedProfileIds.includes(mentor.id)} + isFavorite={favouritedRedProfileIds?.includes(mentor.id)} /> ))} @@ -441,13 +441,13 @@ const curriedFilterFunctions = (filters: FiltersValues) => { }, chosenMentoringTopics(mentor: RedProfile) { if (filters.chosenMentoringTopics.length === 0) return true - return mentor.mentor_mentoringTopics.some((topic) => + return mentor.mentor_mentoringTopics?.some((topic) => filters.chosenMentoringTopics.includes(topic) ) }, chosenToolsAndFrameworks(mentor: RedProfile) { if (filters.chosenToolsAndFrameworks.length === 0) return true - return mentor.mentor_mentoringTopics.some((topic) => + return mentor.mentor_mentoringTopics?.some((topic) => filters.chosenToolsAndFrameworks.includes(topic) ) }, @@ -457,14 +457,14 @@ const curriedFilterFunctions = (filters: FiltersValues) => { }, chosenDesiredRoles(mentor: RedProfile) { if (filters.chosedDesiredRoles.length === 0) return true - return mentor.mentor_professionalExperienceFields.some((field) => + return mentor.mentor_professionalExperienceFields?.some((field) => filters.chosedDesiredRoles.includes(field) ) }, mentorSharesLanguageWithMentee(mentor: RedProfile) { - return mentor.languages.some((lang) => + return mentor.languages?.some((lang) => filters.menteeLanguages.includes(lang) - ) + ) || [] }, menteeMentoringGoalCompatibleWithMentor(mentor: RedProfile) { switch (filters.menteeMentoringGoal) { @@ -508,7 +508,7 @@ const curriedFilterFunctions = (filters: FiltersValues) => { case 'buildingAProfessionalNetwork': case 'entrepreneurshipAndFreelancing': return allMenteeRoleMentoringTopics.some((topic) => - mentor.mentor_mentoringTopics.includes(topic) + mentor.mentor_mentoringTopics?.includes(topic) ) default: From f92466a86ada0ac2d4b73aabc9773a8ad6e3c685 Mon Sep 17 00:00:00 2001 From: Eric Bolikowski Date: Wed, 9 Nov 2022 09:36:43 +0100 Subject: [PATCH 14/25] Revert "Fix undefined values" This reverts commit d2d119402bb552dd23accbe92fce50074435b223. --- .../src/pages/app/find-a-mentor/FindAMentor.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx b/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx index 8f3243248..bae0883f4 100644 --- a/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx +++ b/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx @@ -321,7 +321,7 @@ function FindAMentor({ profile, profileSaveStart }: FindAMentorProps) { toggleFavorite={(item) => toggleFavorites(favouritedRedProfileIds, item) } - isFavorite={favouritedRedProfileIds?.includes(mentor.id)} + isFavorite={favouritedRedProfileIds.includes(mentor.id)} /> ))} @@ -441,13 +441,13 @@ const curriedFilterFunctions = (filters: FiltersValues) => { }, chosenMentoringTopics(mentor: RedProfile) { if (filters.chosenMentoringTopics.length === 0) return true - return mentor.mentor_mentoringTopics?.some((topic) => + return mentor.mentor_mentoringTopics.some((topic) => filters.chosenMentoringTopics.includes(topic) ) }, chosenToolsAndFrameworks(mentor: RedProfile) { if (filters.chosenToolsAndFrameworks.length === 0) return true - return mentor.mentor_mentoringTopics?.some((topic) => + return mentor.mentor_mentoringTopics.some((topic) => filters.chosenToolsAndFrameworks.includes(topic) ) }, @@ -457,14 +457,14 @@ const curriedFilterFunctions = (filters: FiltersValues) => { }, chosenDesiredRoles(mentor: RedProfile) { if (filters.chosedDesiredRoles.length === 0) return true - return mentor.mentor_professionalExperienceFields?.some((field) => + return mentor.mentor_professionalExperienceFields.some((field) => filters.chosedDesiredRoles.includes(field) ) }, mentorSharesLanguageWithMentee(mentor: RedProfile) { - return mentor.languages?.some((lang) => + return mentor.languages.some((lang) => filters.menteeLanguages.includes(lang) - ) || [] + ) }, menteeMentoringGoalCompatibleWithMentor(mentor: RedProfile) { switch (filters.menteeMentoringGoal) { @@ -508,7 +508,7 @@ const curriedFilterFunctions = (filters: FiltersValues) => { case 'buildingAProfessionalNetwork': case 'entrepreneurshipAndFreelancing': return allMenteeRoleMentoringTopics.some((topic) => - mentor.mentor_mentoringTopics?.includes(topic) + mentor.mentor_mentoringTopics.includes(topic) ) default: From fd4241ca3d7cb1bc691a92663b8635265e30cfbe Mon Sep 17 00:00:00 2001 From: Eric Bolikowski Date: Wed, 9 Nov 2022 12:19:24 +0100 Subject: [PATCH 15/25] alternative fix --- .../pages/app/find-a-mentor/FindAMentor.tsx | 45 +++++++++++++++---- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx b/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx index bae0883f4..a94f6936c 100644 --- a/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx +++ b/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx @@ -71,8 +71,7 @@ function FindAMentor({ profile, profileSaveStart }: FindAMentorProps) { getMentors({ nameQuery: nameQuery ?? '', }).then((mentors) => { - console.log(mentors) - setAllFetchedMentors(mentors) + setAllFetchedMentors(ensureNoUndefinedArrayProperties(mentors)) setLoading(false) }) }, [nameQuery]) @@ -321,7 +320,7 @@ function FindAMentor({ profile, profileSaveStart }: FindAMentorProps) { toggleFavorite={(item) => toggleFavorites(favouritedRedProfileIds, item) } - isFavorite={favouritedRedProfileIds.includes(mentor.id)} + isFavorite={favouritedRedProfileIds?.includes(mentor.id)} /> ))} @@ -441,13 +440,13 @@ const curriedFilterFunctions = (filters: FiltersValues) => { }, chosenMentoringTopics(mentor: RedProfile) { if (filters.chosenMentoringTopics.length === 0) return true - return mentor.mentor_mentoringTopics.some((topic) => + return mentor.mentor_mentoringTopics?.some((topic) => filters.chosenMentoringTopics.includes(topic) ) }, chosenToolsAndFrameworks(mentor: RedProfile) { if (filters.chosenToolsAndFrameworks.length === 0) return true - return mentor.mentor_mentoringTopics.some((topic) => + return mentor.mentor_mentoringTopics?.some((topic) => filters.chosenToolsAndFrameworks.includes(topic) ) }, @@ -457,13 +456,15 @@ const curriedFilterFunctions = (filters: FiltersValues) => { }, chosenDesiredRoles(mentor: RedProfile) { if (filters.chosedDesiredRoles.length === 0) return true - return mentor.mentor_professionalExperienceFields.some((field) => + return mentor.mentor_professionalExperienceFields?.some((field) => filters.chosedDesiredRoles.includes(field) ) }, mentorSharesLanguageWithMentee(mentor: RedProfile) { - return mentor.languages.some((lang) => - filters.menteeLanguages.includes(lang) + return ( + mentor.languages?.some((lang) => + filters.menteeLanguages.includes(lang) + ) || [] ) }, menteeMentoringGoalCompatibleWithMentor(mentor: RedProfile) { @@ -508,7 +509,7 @@ const curriedFilterFunctions = (filters: FiltersValues) => { case 'buildingAProfessionalNetwork': case 'entrepreneurshipAndFreelancing': return allMenteeRoleMentoringTopics.some((topic) => - mentor.mentor_mentoringTopics.includes(topic) + mentor.mentor_mentoringTopics?.includes(topic) ) default: @@ -520,3 +521,29 @@ const curriedFilterFunctions = (filters: FiltersValues) => { }, } } + +function ensureNoUndefinedArrayProperties(mentors: RedProfile[]) { + const keys = [ + 'languages', + 'categories', + 'favouritedRedProfileIds', + 'mentor_mentoringTopics', + 'mentor_mentoringGoals', + 'mentor_professionalExperienceFields', + 'mentee_overarchingMentoringTopics', + 'mentee_primaryRole_mentoringTopics', + 'mentee_secondaryRole_mentoringTopics', + 'mentee_toolsAndFrameworks_mentoringTopics', + ] + const fixedMentors = mentors.map((mentor) => { + keys.forEach((key) => { + if (mentor[key] === undefined) { + mentor[key] = [] + } + }) + + return mentor + }) + + return fixedMentors +} From e0d5abe8061275ef4039a4cea412ee0bc6d8dc48 Mon Sep 17 00:00:00 2001 From: Eric Bolikowski Date: Fri, 11 Nov 2022 09:54:25 +0100 Subject: [PATCH 16/25] ensure RedProfile of mentee user has no undefined array properties --- .../pages/app/find-a-mentor/FindAMentor.tsx | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx b/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx index a94f6936c..6409bd53f 100644 --- a/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx +++ b/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx @@ -45,6 +45,7 @@ interface FindAMentorProps { } function FindAMentor({ profile, profileSaveStart }: FindAMentorProps) { + profile = ensureNoUndefinedArrayPropertiesInProfile(profile) const { Loading, isLoading, setLoading } = useLoading() const [allFetchedMentors, setAllFetchedMentors] = useState([]) const [ @@ -71,7 +72,7 @@ function FindAMentor({ profile, profileSaveStart }: FindAMentorProps) { getMentors({ nameQuery: nameQuery ?? '', }).then((mentors) => { - setAllFetchedMentors(ensureNoUndefinedArrayProperties(mentors)) + setAllFetchedMentors(ensureNoUndefinedArrayPropertiesInProfiles(mentors)) setLoading(false) }) }, [nameQuery]) @@ -320,7 +321,7 @@ function FindAMentor({ profile, profileSaveStart }: FindAMentorProps) { toggleFavorite={(item) => toggleFavorites(favouritedRedProfileIds, item) } - isFavorite={favouritedRedProfileIds?.includes(mentor.id)} + isFavorite={favouritedRedProfileIds.includes(mentor.id)} /> ))} @@ -440,13 +441,13 @@ const curriedFilterFunctions = (filters: FiltersValues) => { }, chosenMentoringTopics(mentor: RedProfile) { if (filters.chosenMentoringTopics.length === 0) return true - return mentor.mentor_mentoringTopics?.some((topic) => + return mentor.mentor_mentoringTopics.some((topic) => filters.chosenMentoringTopics.includes(topic) ) }, chosenToolsAndFrameworks(mentor: RedProfile) { if (filters.chosenToolsAndFrameworks.length === 0) return true - return mentor.mentor_mentoringTopics?.some((topic) => + return mentor.mentor_mentoringTopics.some((topic) => filters.chosenToolsAndFrameworks.includes(topic) ) }, @@ -456,13 +457,13 @@ const curriedFilterFunctions = (filters: FiltersValues) => { }, chosenDesiredRoles(mentor: RedProfile) { if (filters.chosedDesiredRoles.length === 0) return true - return mentor.mentor_professionalExperienceFields?.some((field) => + return mentor.mentor_professionalExperienceFields.some((field) => filters.chosedDesiredRoles.includes(field) ) }, mentorSharesLanguageWithMentee(mentor: RedProfile) { return ( - mentor.languages?.some((lang) => + mentor.languages.some((lang) => filters.menteeLanguages.includes(lang) ) || [] ) @@ -509,7 +510,7 @@ const curriedFilterFunctions = (filters: FiltersValues) => { case 'buildingAProfessionalNetwork': case 'entrepreneurshipAndFreelancing': return allMenteeRoleMentoringTopics.some((topic) => - mentor.mentor_mentoringTopics?.includes(topic) + mentor.mentor_mentoringTopics.includes(topic) ) default: @@ -522,7 +523,7 @@ const curriedFilterFunctions = (filters: FiltersValues) => { } } -function ensureNoUndefinedArrayProperties(mentors: RedProfile[]) { +function ensureNoUndefinedArrayPropertiesInProfile(mentor: RedProfile) { const keys = [ 'languages', 'categories', @@ -535,15 +536,18 @@ function ensureNoUndefinedArrayProperties(mentors: RedProfile[]) { 'mentee_secondaryRole_mentoringTopics', 'mentee_toolsAndFrameworks_mentoringTopics', ] - const fixedMentors = mentors.map((mentor) => { - keys.forEach((key) => { - if (mentor[key] === undefined) { - mentor[key] = [] - } - }) - return mentor + keys.forEach((key) => { + if (mentor[key] === undefined) { + mentor[key] = [] + } }) + return mentor +} + +function ensureNoUndefinedArrayPropertiesInProfiles(mentors: RedProfile[]) { + const fixedMentors = mentors.map(ensureNoUndefinedArrayPropertiesInProfile) + return fixedMentors } From 6f07152c4b435dcdd11fdf3f8568dceb80ec4bd8 Mon Sep 17 00:00:00 2001 From: helloanil Date: Mon, 21 Nov 2022 08:48:38 +0100 Subject: [PATCH 17/25] Fix mentoringGoals field name with mentor_mentoringGoals --- .../components/organisms/EditableMentoringGoals.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/redi-connect/src/components/organisms/EditableMentoringGoals.tsx b/apps/redi-connect/src/components/organisms/EditableMentoringGoals.tsx index b51668d72..70dc75f0f 100644 --- a/apps/redi-connect/src/components/organisms/EditableMentoringGoals.tsx +++ b/apps/redi-connect/src/components/organisms/EditableMentoringGoals.tsx @@ -16,14 +16,14 @@ import ReadMentoringGoals from '../molecules/ReadMentoringGoals' export interface FormValues { isMentor: boolean - mentoringGoals: string[] + mentor_mentoringGoals: string[] } const formMentoringGoals = objectEntries(MENTORING_GOALS) interface Props { profile: RedProfile | undefined - profileSaveStart: Function + profileSaveStart: (arg0: Partial) => void } const validationSchema = Yup.object({ @@ -47,7 +47,7 @@ const EditableMentoringGoals = ({ profile, profileSaveStart }: Props) => { const initialValues: FormValues = { isMentor, - mentoringGoals: mentoringGoals || [], + mentor_mentoringGoals: mentoringGoals || [], } const formik = useFormik({ @@ -57,7 +57,7 @@ const EditableMentoringGoals = ({ profile, profileSaveStart }: Props) => { onSubmit: submitForm, }) - const { mentoringGoals: selectedMentoringGoals } = formik.values + const { mentor_mentoringGoals: selectedMentoringGoals } = formik.values const mentoringGoalsChange = (e: any) => { e.persist() @@ -70,8 +70,8 @@ const EditableMentoringGoals = ({ profile, profileSaveStart }: Props) => { (cat: any) => cat !== value ) } - formik.setFieldValue('mentoringGoals', newMentoringGoals) - formik.setFieldTouched('mentoringGoals', true, false) + formik.setFieldValue('mentor_mentoringGoals', newMentoringGoals) + formik.setFieldTouched('mentor_mentoringGoals', true, false) } return ( From 84d003430fccdfaf1ffe1b443bc4313164050ce5 Mon Sep 17 00:00:00 2001 From: kate Date: Wed, 23 Nov 2022 12:07:57 +0100 Subject: [PATCH 18/25] Fix mentors professionalExperience field, ensure mentee provides required information to display list of mentors --- .../src/assets/locales/en/translation.json | 3 ++- .../EditableProfessionalExperienceFields.tsx | 10 +++++----- .../src/components/templates/LoggedIn.tsx | 8 ++++++++ .../pages/app/find-a-mentor/FindAMentor.tsx | 19 +++++++++++++++++-- .../src/pages/app/find-a-mentor/utils.ts | 15 +++++++++++++++ 5 files changed, 47 insertions(+), 8 deletions(-) diff --git a/apps/redi-connect/src/assets/locales/en/translation.json b/apps/redi-connect/src/assets/locales/en/translation.json index 0192236d3..67516328a 100644 --- a/apps/redi-connect/src/assets/locales/en/translation.json +++ b/apps/redi-connect/src/assets/locales/en/translation.json @@ -186,7 +186,8 @@ "profile": { "notification": { "pendingMentor": "Thanks for signing up! We are reviewing your profile and will send you an email once we're done. Students will be able to apply to become your mentee once your account is active.", - "pendingMentee": "Thanks for signing up! Please schedule a time for an onboarding session with us. You find the link in the email you received. Youโ€™ll be able to find a mentor once your account is active.", + "pendingMentee": "Thanks for signing up! Please schedule a time for an onboarding session with us. You find the link in the email you received. Meanwhile, we encourage you to fill in your profile.", + "missingMentoringGoal": "Welcome to ReDI Connect! Please select a mentoring goal and a primary role you would like to be mentored on. You will be able to find a mentor once your profile is complete.", "deactivatedMentee": "Dear {{ name }}, your ReDI Connect profile has been deactivated by the Career Support Team. This could be for a number of reasons. If you think this has been done by mistake, please contact Paulina at {{ email }}. Thank you!", "deactivatedMentor": "Dear {{ name }}, your ReDI Connect profile has been deactivated by the Career Support Team. Likely you have not been active for a while. This means you are not visible to prospective mentees. If you want to become active as a mentor again, please contact Miriam at {{ email }}. Speak soon!" } diff --git a/apps/redi-connect/src/components/organisms/EditableProfessionalExperienceFields.tsx b/apps/redi-connect/src/components/organisms/EditableProfessionalExperienceFields.tsx index aaa623281..e90ccd1d2 100644 --- a/apps/redi-connect/src/components/organisms/EditableProfessionalExperienceFields.tsx +++ b/apps/redi-connect/src/components/organisms/EditableProfessionalExperienceFields.tsx @@ -16,7 +16,7 @@ import ReadProfessionalExperienceFields from '../molecules/ReadProfessionalExper export interface FormValues { isMentor: boolean - professionalExperienceFields: string[] + mentor_professionalExperienceFields: string[] } const formProfessionalExperienceFields = objectEntries(FIELDS_OF_EXPERTISE) @@ -46,7 +46,7 @@ const EditableProfessionalExperienceFields = ({ const initialValues: FormValues = { isMentor, - professionalExperienceFields: professionalExperienceFields || [], + mentor_professionalExperienceFields: professionalExperienceFields || [], } const formik = useFormik({ @@ -55,7 +55,7 @@ const EditableProfessionalExperienceFields = ({ onSubmit: submitForm, }) - const { professionalExperienceFields: selectedProfessionalExperienceFields } = + const { mentor_professionalExperienceFields: selectedProfessionalExperienceFields } = formik.values const professionalExperienceFieldsChange = (e: any) => { @@ -70,10 +70,10 @@ const EditableProfessionalExperienceFields = ({ selectedProfessionalExperienceFields.filter((cat: any) => cat !== value) } formik.setFieldValue( - 'professionalExperienceFields', + 'mentor_professionalExperienceFields', newProfessionalExperienceFields ) - formik.setFieldTouched('professionalExperienceFields', true, false) + formik.setFieldTouched('mentor_professionalExperienceFields', true, false) } return ( diff --git a/apps/redi-connect/src/components/templates/LoggedIn.tsx b/apps/redi-connect/src/components/templates/LoggedIn.tsx index 353104285..1499981c2 100644 --- a/apps/redi-connect/src/components/templates/LoggedIn.tsx +++ b/apps/redi-connect/src/components/templates/LoggedIn.tsx @@ -24,6 +24,7 @@ import { import { useTranslation } from 'react-i18next' import Footer from '../organisms/Footer' import { RedMatch } from '@talent-connect/shared-types' +import { ensureMenteeProfileIsComplete } from '../../../../redi-connect/src/pages/app/find-a-mentor/utils' interface Props { loading: boolean @@ -72,6 +73,8 @@ const LoggedIn = ({ history.push(`/app/mentorships/${redMatchId}`) } + const isMenteeProfileComplete = ensureMenteeProfileIsComplete(profile) + return ( <> @@ -96,6 +99,11 @@ const LoggedIn = ({ {t('loggedInArea.profile.notification.pendingMentor')} )} + {profile.userType === 'mentee' && !isMenteeProfileComplete && ( + + {t('loggedInArea.profile.notification.missingMentoringGoal')} + + )} {profile.userType === 'mentee' && !profile.userActivated && ( {t('loggedInArea.profile.notification.deactivatedMentee', { diff --git a/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx b/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx index 6409bd53f..d798b66bf 100644 --- a/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx +++ b/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx @@ -29,7 +29,7 @@ import { FieldOfExperienceKey, } from '@talent-connect/shared-config' import './FindAMentor.scss' -import { toggleValueInArray } from './utils' +import { toggleValueInArray, ensureMenteeProfileIsComplete } from './utils' import { StringParam, useQueryParams, @@ -117,7 +117,9 @@ function FindAMentor({ profile, profileSaveStart }: FindAMentorProps) { })) } - if (profile.userActivated !== true) return + const isMenteeProfileComplete = ensureMenteeProfileIsComplete(profile) + + if (profile.userActivated !== true || !isMenteeProfileComplete) return const eligibleMentors = allFetchedMentors .filter((mentor) => mentor.currentFreeMenteeSpots > 0) @@ -470,6 +472,11 @@ const curriedFilterFunctions = (filters: FiltersValues) => { }, menteeMentoringGoalCompatibleWithMentor(mentor: RedProfile) { switch (filters.menteeMentoringGoal) { + /** + * When mentee has one of the following goals from a mentorship, + * we filter the mentors with relevant professional experience with mentee's + * desired roles + */ case 'tutoringInAParticularSkillTool': case 'preparationForACertificationInterview': case 'careerOrientatioPlanning': @@ -480,9 +487,15 @@ const curriedFilterFunctions = (filters: FiltersValues) => { ) return false + /** + * If the mentee has Primary or Secondary Skills selected, we match those + * with the mentor's mentoringTopics to filter mentors. If they are not selected + * we don't filter. + */ case 'tutoringInAParticularSkillTool': case 'preparationForACertificationInterview': if ( + allMenteeRoleMentoringTopics.length > 0 && !allMenteeRoleMentoringTopics.some((topic) => mentor.mentor_mentoringTopics.includes(topic) ) @@ -531,12 +544,14 @@ function ensureNoUndefinedArrayPropertiesInProfile(mentor: RedProfile) { 'mentor_mentoringTopics', 'mentor_mentoringGoals', 'mentor_professionalExperienceFields', + 'mentee_mentoringGoal', 'mentee_overarchingMentoringTopics', 'mentee_primaryRole_mentoringTopics', 'mentee_secondaryRole_mentoringTopics', 'mentee_toolsAndFrameworks_mentoringTopics', ] + keys.forEach((key) => { if (mentor[key] === undefined) { mentor[key] = [] diff --git a/apps/redi-connect/src/pages/app/find-a-mentor/utils.ts b/apps/redi-connect/src/pages/app/find-a-mentor/utils.ts index 5c2d5363c..0455a27b3 100644 --- a/apps/redi-connect/src/pages/app/find-a-mentor/utils.ts +++ b/apps/redi-connect/src/pages/app/find-a-mentor/utils.ts @@ -1,4 +1,19 @@ +import { RedProfile } from '@talent-connect/shared-types' + export function toggleValueInArray(array: Array, value: T) { if (array.includes(value)) return array.filter((val) => val !== value) else return [...array, value] } + +// Fix for newly registered mentee users: +// Ensuring that a mentee filled their profile with required information before displaying list of mentors +const isString = (value: any) => typeof value === 'string' +const isFulfilledString = (string: any) => isString(string) && string.length > 0 + +const arrayIsNotEmpty = (array: any) => Array.isArray(array) && array.length > 0 +const isObjectValueFulfilled = (objectValue: any) => arrayIsNotEmpty(objectValue) || isFulfilledString(objectValue) + +export const ensureMenteeProfileIsComplete = ({ mentee_mentoringGoal, mentee_primaryRole_fieldOfExpertise }: RedProfile) => { + const isMenteeProfileComplete = isObjectValueFulfilled(mentee_mentoringGoal) && isObjectValueFulfilled(mentee_primaryRole_fieldOfExpertise) + return isMenteeProfileComplete +} From f8ab46428b0e692ae05ecbba912dc86d281be957 Mon Sep 17 00:00:00 2001 From: kate Date: Thu, 1 Dec 2022 15:23:06 +0100 Subject: [PATCH 19/25] Mark required and optional fields, fix typos --- .../EditableMentoringGoalTopics.tsx | 16 ++++++++-------- .../ReadMentoringGoalTopics.tsx | 12 ++++++------ .../me-mentee-profile/ReadToolsAndFrameworks.tsx | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoalTopics.tsx b/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoalTopics.tsx index 979ce51f7..0a37f51d2 100644 --- a/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoalTopics.tsx +++ b/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoalTopics.tsx @@ -37,10 +37,10 @@ interface Props { const validationSchema = Yup.object({ mentee_mentoringGoal: Yup.string() .nullable() - .required('Select a mentoring goal'), + .required('Please select a mentoring goal'), mentee_primaryRole_fieldOfExpertise: Yup.string() .nullable() - .required('Select a role'), + .required('Please select a role'), mentee_overarchingMentoringTopics: Yup.array().max( 3, 'You can select up to three topics' @@ -116,7 +116,7 @@ const EditableMentoringGoalTopics = ({ profile, profileSaveStart }: Props) => { Goal { Topics { Primary role { Skills { Secondary role { Skills { ) : ( - The most important goal you would like to adress with your - mentor + The most important goal to address with your + mentor. )} @@ -53,7 +53,7 @@ const ReadMentoringGoalTopics = ({ profile, caption }: ReadMentoringProps) => { /> ) : ( - General topics you would like to me mentored on + General topics you would like to be mentored on. )} @@ -84,7 +84,7 @@ const ReadMentoringGoalTopics = ({ profile, caption }: ReadMentoringProps) => { /> ) : ( - Role-related skills you would like to mentored on (optional) + Role-related skills you would like to be mentored on. )} @@ -93,7 +93,7 @@ const ReadMentoringGoalTopics = ({ profile, caption }: ReadMentoringProps) => { - Secondary role (optional) + Secondary role {mentee_secondaryRole_fieldOfExpertise ? ( { /> ) : ( - Role-related skills you would like to mentored on (optional) + Role-related skills you would like to be mentored on. )} diff --git a/apps/redi-connect/src/pages/app/me/me-mentee-profile/ReadToolsAndFrameworks.tsx b/apps/redi-connect/src/pages/app/me/me-mentee-profile/ReadToolsAndFrameworks.tsx index 686d5d4f0..98928f2cc 100644 --- a/apps/redi-connect/src/pages/app/me/me-mentee-profile/ReadToolsAndFrameworks.tsx +++ b/apps/redi-connect/src/pages/app/me/me-mentee-profile/ReadToolsAndFrameworks.tsx @@ -26,7 +26,7 @@ const ReadToolsAndFrameworks = ({ profile, caption }: ReadMentoringProps) => { /> ) : ( - Please select tool and technologies you are particularly interested in + Please select tools and technologies you are particularly interested in (max 3). )} From 5cd01416ddba72403e76285b68119d19ced5fdc5 Mon Sep 17 00:00:00 2001 From: kate Date: Thu, 1 Dec 2022 18:16:18 +0100 Subject: [PATCH 20/25] Hide Application page if mentee profile is not complete --- .../redi-connect/src/pages/app/applications/Applications.tsx | 5 ++++- .../app/me/me-mentee-profile/EditableToolsAndFrameworks.tsx | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/redi-connect/src/pages/app/applications/Applications.tsx b/apps/redi-connect/src/pages/app/applications/Applications.tsx index ea98e8bee..977e90d9e 100644 --- a/apps/redi-connect/src/pages/app/applications/Applications.tsx +++ b/apps/redi-connect/src/pages/app/applications/Applications.tsx @@ -9,6 +9,7 @@ import { connect } from 'react-redux' import { RedMatch } from '@talent-connect/shared-types' import { useHistory } from 'react-router-dom' import { getRedProfileFromLocalStorage } from '../../../services/auth/auth' +import { ensureMenteeProfileIsComplete } from '../find-a-mentor/utils' interface Props { applicants: RedMatch[] @@ -18,7 +19,9 @@ function Applications({ applicants }: Props) { const history = useHistory() const profile = getRedProfileFromLocalStorage() - if (profile.userActivated !== true) return + const isMenteeProfileComplete = ensureMenteeProfileIsComplete(profile) + + if (profile.userActivated !== true || !isMenteeProfileComplete) return return ( diff --git a/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableToolsAndFrameworks.tsx b/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableToolsAndFrameworks.tsx index 92b4a3948..5821fa8dc 100644 --- a/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableToolsAndFrameworks.tsx +++ b/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableToolsAndFrameworks.tsx @@ -86,7 +86,7 @@ const EditableToolsAndFrameworks = ({ profile, profileSaveStart }: Props) => { className="mentoring" > - Please select tool and technologies you are particularly interested in + Please select tools and technologies you are particularly interested in (max 3). From f9f826eb27dc03498b2bbae56866c34d6e50685c Mon Sep 17 00:00:00 2001 From: kate Date: Tue, 6 Dec 2022 11:53:53 +0100 Subject: [PATCH 21/25] Fixes --- .../src/assets/locales/en/translation.json | 2 +- .../EditableMentoringGoalTopics.tsx | 17 +++++++++-------- .../src/lib/mentoring-data-lists.ts | 10 ++++++++++ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/apps/redi-connect/src/assets/locales/en/translation.json b/apps/redi-connect/src/assets/locales/en/translation.json index 67516328a..4e3d48dd8 100644 --- a/apps/redi-connect/src/assets/locales/en/translation.json +++ b/apps/redi-connect/src/assets/locales/en/translation.json @@ -187,7 +187,7 @@ "notification": { "pendingMentor": "Thanks for signing up! We are reviewing your profile and will send you an email once we're done. Students will be able to apply to become your mentee once your account is active.", "pendingMentee": "Thanks for signing up! Please schedule a time for an onboarding session with us. You find the link in the email you received. Meanwhile, we encourage you to fill in your profile.", - "missingMentoringGoal": "Welcome to ReDI Connect! Please select a mentoring goal and a primary role you would like to be mentored on. You will be able to find a mentor once your profile is complete.", + "missingMentoringGoal": "Welcome to ReDI Connect! Please fill in your profile and select a mentoring goal and a primary role you would like to be mentored on. You will be able to find a mentor once your profile is complete.", "deactivatedMentee": "Dear {{ name }}, your ReDI Connect profile has been deactivated by the Career Support Team. This could be for a number of reasons. If you think this has been done by mistake, please contact Paulina at {{ email }}. Thank you!", "deactivatedMentor": "Dear {{ name }}, your ReDI Connect profile has been deactivated by the Career Support Team. Likely you have not been active for a while. This means you are not visible to prospective mentees. If you want to become active as a mentor again, please contact Miriam at {{ email }}. Speak soon!" } diff --git a/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoalTopics.tsx b/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoalTopics.tsx index 0a37f51d2..1d809f3f3 100644 --- a/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoalTopics.tsx +++ b/apps/redi-connect/src/pages/app/me/me-mentee-profile/EditableMentoringGoalTopics.tsx @@ -114,9 +114,9 @@ const EditableMentoringGoalTopics = ({ profile, profileSaveStart }: Props) => { - Goal + Goal* { Topics { - Primary role + Primary role* { Skills { Secondary role { Skills ({ value, label }) ) + const formMentoringTopicsInGroup = (group) => MENTORING_TOPICS.filter((topic) => topic.group === group).map( ({ id, label }) => ({ value: id, label }) diff --git a/libs/shared-config/src/lib/mentoring-data-lists.ts b/libs/shared-config/src/lib/mentoring-data-lists.ts index 007180a03..72e3c2cc0 100644 --- a/libs/shared-config/src/lib/mentoring-data-lists.ts +++ b/libs/shared-config/src/lib/mentoring-data-lists.ts @@ -313,6 +313,16 @@ export const MENTORING_TOPICS = [ label: 'Value metrics', group: 'productAndProjectManagement', }, + { + id: 'Front-end development', + label: 'Front-end Development', + group: 'softwareDevelopment', + }, + { + id: 'Back-end development', + label: 'Back-end Development', + group: 'softwareDevelopment', + }, { id: 'Android Mobile Development', label: 'Android Mobile Development', From 6b8277c7349fd7eae6034157dddfa6b8dd455332 Mon Sep 17 00:00:00 2001 From: kate Date: Fri, 9 Dec 2022 17:40:31 +0100 Subject: [PATCH 22/25] Fix the blank Applications page for mentors --- .../src/pages/app/applications/Applications.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/redi-connect/src/pages/app/applications/Applications.tsx b/apps/redi-connect/src/pages/app/applications/Applications.tsx index 977e90d9e..b9c3ab11f 100644 --- a/apps/redi-connect/src/pages/app/applications/Applications.tsx +++ b/apps/redi-connect/src/pages/app/applications/Applications.tsx @@ -21,7 +21,13 @@ function Applications({ applicants }: Props) { const isMenteeProfileComplete = ensureMenteeProfileIsComplete(profile) - if (profile.userActivated !== true || !isMenteeProfileComplete) return + console.log('isMenteeProfileComplete', isMenteeProfileComplete) + + if ( + profile.userActivated !== true || + (profile.userType === 'mentee' && !isMenteeProfileComplete) + ) + return return ( From ba15b2ad5e5b262c3dead74d7e19518fcd690a8f Mon Sep 17 00:00:00 2001 From: kate Date: Fri, 9 Dec 2022 17:43:13 +0100 Subject: [PATCH 23/25] Delete log --- apps/redi-connect/src/pages/app/applications/Applications.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/redi-connect/src/pages/app/applications/Applications.tsx b/apps/redi-connect/src/pages/app/applications/Applications.tsx index b9c3ab11f..df3385b06 100644 --- a/apps/redi-connect/src/pages/app/applications/Applications.tsx +++ b/apps/redi-connect/src/pages/app/applications/Applications.tsx @@ -21,8 +21,6 @@ function Applications({ applicants }: Props) { const isMenteeProfileComplete = ensureMenteeProfileIsComplete(profile) - console.log('isMenteeProfileComplete', isMenteeProfileComplete) - if ( profile.userActivated !== true || (profile.userType === 'mentee' && !isMenteeProfileComplete) From 4165ccfae2641c3d65225e26e0e5ed7e74943506 Mon Sep 17 00:00:00 2001 From: Eric Bolikowski Date: Wed, 14 Dec 2022 08:21:58 +0000 Subject: [PATCH 24/25] chore: add missing `as const` --- libs/shared-config/src/lib/mentoring-data-lists.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/shared-config/src/lib/mentoring-data-lists.ts b/libs/shared-config/src/lib/mentoring-data-lists.ts index 72e3c2cc0..0db11ddbb 100644 --- a/libs/shared-config/src/lib/mentoring-data-lists.ts +++ b/libs/shared-config/src/lib/mentoring-data-lists.ts @@ -420,7 +420,7 @@ export const MENTORING_TOPICS = [ { id: 'Zephyr', label: 'Zephyr', group: 'toolsAndFrameworks' }, { id: 'VMWare', label: 'VMWare', group: 'toolsAndFrameworks' }, { id: 'Virtual Box', label: 'Virtual Box', group: 'toolsAndFrameworks' }, -] +] as const export type MentoringTopicKey = typeof MENTORING_TOPICS[number]['id'] export type MentoringTopicLabel = typeof MENTORING_TOPICS[number]['label'] From 92bab9297af839b8885d8f5f7a64f1a2a408a00e Mon Sep 17 00:00:00 2001 From: Eric Bolikowski Date: Wed, 8 Mar 2023 15:25:13 +0700 Subject: [PATCH 25/25] fix(con): build error through proper type assertion --- .../pages/app/find-a-mentor/FindAMentor.tsx | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx b/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx index d798b66bf..9152bdee7 100644 --- a/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx +++ b/apps/redi-connect/src/pages/app/find-a-mentor/FindAMentor.tsx @@ -48,17 +48,7 @@ function FindAMentor({ profile, profileSaveStart }: FindAMentorProps) { profile = ensureNoUndefinedArrayPropertiesInProfile(profile) const { Loading, isLoading, setLoading } = useLoading() const [allFetchedMentors, setAllFetchedMentors] = useState([]) - const [ - { - nameQuery, - onlyFavorites, - mentoringTopics, - mentoringTopicsToolsAndFrameworks, - locations, - roles, - }, - setQuery, - ] = useQueryParams({ + const [queryParams, setQuery] = useQueryParams({ nameQuery: withDefault(StringParam, undefined), onlyFavorites: withDefault(BooleanParam, undefined), mentoringTopics: withDefault(ArrayParam, []), @@ -67,6 +57,15 @@ function FindAMentor({ profile, profileSaveStart }: FindAMentorProps) { roles: withDefault(ArrayParam, []), }) + const nameQuery = queryParams.nameQuery + const onlyFavorites = queryParams.onlyFavorites + const mentoringTopics = + queryParams.mentoringTopics as Array + const mentoringTopicsToolsAndFrameworks = + queryParams.mentoringTopicsToolsAndFrameworks as Array + const locations = queryParams.locations + const roles = queryParams.roles + useEffect(() => { setLoading(true) getMentors({ @@ -118,8 +117,9 @@ function FindAMentor({ profile, profileSaveStart }: FindAMentorProps) { } const isMenteeProfileComplete = ensureMenteeProfileIsComplete(profile) - - if (profile.userActivated !== true || !isMenteeProfileComplete) return + + if (profile.userActivated !== true || !isMenteeProfileComplete) + return const eligibleMentors = allFetchedMentors .filter((mentor) => mentor.currentFreeMenteeSpots > 0) @@ -551,7 +551,6 @@ function ensureNoUndefinedArrayPropertiesInProfile(mentor: RedProfile) { 'mentee_toolsAndFrameworks_mentoringTopics', ] - keys.forEach((key) => { if (mentor[key] === undefined) { mentor[key] = []