From 8d2c3160f361bfc1bd79fa708cc5cbda90234db8 Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Thu, 15 Jan 2026 01:29:27 +0530 Subject: [PATCH 1/4] fix: lint --- .../ModifyWorkExpirenceModal.tsx | 174 ++++++++++++++++-- .../work-expirence/WorkExpirence.tsx | 84 ++++++++- .../WorkExpirenceCard.module.scss | 35 ++++ .../WorkExpirenceCard/WorkExpirenceCard.tsx | 49 ++++- .../standard-skills.service.ts | 20 ++ 5 files changed, 339 insertions(+), 23 deletions(-) diff --git a/src/apps/profiles/src/member-profile/work-expirence/ModifyWorkExpirenceModal/ModifyWorkExpirenceModal.tsx b/src/apps/profiles/src/member-profile/work-expirence/ModifyWorkExpirenceModal/ModifyWorkExpirenceModal.tsx index fe56f0b97..a15fd5098 100644 --- a/src/apps/profiles/src/member-profile/work-expirence/ModifyWorkExpirenceModal/ModifyWorkExpirenceModal.tsx +++ b/src/apps/profiles/src/member-profile/work-expirence/ModifyWorkExpirenceModal/ModifyWorkExpirenceModal.tsx @@ -1,17 +1,18 @@ /* eslint-disable complexity */ -import { Dispatch, FC, MutableRefObject, SetStateAction, useRef, useState } from 'react' +import { ChangeEvent, Dispatch, FC, MutableRefObject, SetStateAction, useEffect, useRef, useState } from 'react' import { bind, sortBy, trim } from 'lodash' import { toast } from 'react-toastify' import classNames from 'classnames' -import { BaseModal, Button, IconOutline, InputDatePicker, InputSelect, InputText } from '~/libs/ui' +import { BaseModal, Button, IconOutline, InputDatePicker, InputSelect, InputText, InputTextarea } from '~/libs/ui' import { updateDeleteOrCreateMemberTraitAsync, UserProfile, UserTrait, UserTraitCategoryNames, UserTraitIds, } from '~/libs/core' -import { getIndustryOptionLabel, getIndustryOptionValue, INDUSTRIES_OPTIONS } from '~/libs/shared' +import { getIndustryOptionLabel, getIndustryOptionValue, INDUSTRIES_OPTIONS, InputSkillSelector } from '~/libs/shared' +import { fetchSkillsByIds } from '~/libs/shared/lib/services/standard-skills' import { WorkExpirenceCard } from '../WorkExpirenceCard' @@ -32,10 +33,10 @@ const ModifyWorkExpirenceModal: FC = (props: Modi = useState(props.workExpirence?.length === 0 || false) const [formValues, setFormValues]: [ - { [key: string]: string | boolean | Date | undefined }, - Dispatch> + { [key: string]: string | boolean | Date | any[] | undefined }, + Dispatch> ] - = useState<{ [key: string]: string | boolean | Date | undefined }>({}) + = useState<{ [key: string]: string | boolean | Date | any[] | undefined }>({}) const [formErrors, setFormErrors]: [ { [key: string]: string }, @@ -56,6 +57,86 @@ const ModifyWorkExpirenceModal: FC = (props: Modi ] = useState(props.workExpirence) + const [skillNamesMap, setSkillNamesMap] = useState>({}) + const [loadingSkills, setLoadingSkills] = useState(false) + const fetchedSkillIdsRef = useRef>(new Set()) + + useEffect(() => { + if (!workExpirence) { + setLoadingSkills(false) + return + } + + const allSkillIds = new Set() + workExpirence.forEach((work: UserTrait) => { + if (work.associatedSkills && Array.isArray(work.associatedSkills)) { + work.associatedSkills.forEach((skillId: string) => { + if (skillId && typeof skillId === 'string') { + allSkillIds.add(skillId) + } + }) + } + }) + + if (allSkillIds.size > 0) { + const skillIdsToFetch = Array.from(allSkillIds) + .filter(id => !fetchedSkillIdsRef.current.has(id)) + + if (skillIdsToFetch.length > 0) { + setLoadingSkills(true) + skillIdsToFetch.forEach(id => fetchedSkillIdsRef.current.add(id)) + + fetchSkillsByIds(skillIdsToFetch) + .then(skills => { + setSkillNamesMap(prevMap => { + const newMap: Record = { ...prevMap } + skills.forEach(skill => { + if (skill.id && skill.name) { + newMap[skill.id] = skill.name + } + }) + skillIdsToFetch.forEach(skillId => { + if (!newMap[skillId]) { + newMap[skillId] = skillId + } + }) + return newMap + }) + }) + .catch(() => { + setSkillNamesMap(prevMap => { + const fallbackMap: Record = { ...prevMap } + skillIdsToFetch.forEach(skillId => { + if (!fallbackMap[skillId]) { + fallbackMap[skillId] = skillId + } + }) + return fallbackMap + }) + }) + .finally(() => { + setLoadingSkills(false) + }) + } else { + setLoadingSkills(false) + } + } else { + // No skills to fetch + setLoadingSkills(false) + } + }, [workExpirence]) + + const areSkillsLoaded = (work: UserTrait): boolean => { + if (!work.associatedSkills || !Array.isArray(work.associatedSkills) || work.associatedSkills.length === 0) { + return true + } + + return work.associatedSkills.every((skillId: string) => { + const skillName = skillNamesMap[skillId] + return skillName && skillName !== skillId + }) + } + const industryOptions: any = sortBy(INDUSTRIES_OPTIONS) .map(v => ({ label: getIndustryOptionLabel(v), @@ -89,13 +170,16 @@ const ModifyWorkExpirenceModal: FC = (props: Modi }) } - function handleFormValueChange(key: string, event: React.ChangeEvent): void { + function handleFormValueChange( + key: string, + event: React.ChangeEvent, + ): void { let value: string | boolean | Date | undefined const oldFormValues = { ...formValues } switch (key) { case 'currentlyWorking': - value = event.target.checked + value = (event.target as HTMLInputElement).checked if (value) { oldFormValues.endDate = undefined } @@ -116,6 +200,17 @@ const ModifyWorkExpirenceModal: FC = (props: Modi }) } + function handleSkillsChange(event: ChangeEvent): void { + const selectedSkills = (event.target as any).value || [] + setFormValues({ + ...formValues, + associatedSkills: selectedSkills.map((skill: any) => ({ + id: skill.value || skill.id, + name: skill.label || skill.name, + })), + }) + } + function resetForm(): void { setFormValues({}) setFormErrors({}) @@ -173,9 +268,10 @@ const ModifyWorkExpirenceModal: FC = (props: Modi : undefined const updatedWorkExpirence: UserTrait = { - cityTown: formValues.city, + associatedSkills: (formValues.associatedSkills as any[])?.map((s: any) => s.id || s) || [], company: companyName, companyName, + description: (formValues.description as string) || undefined, endDate: endDateIso, industry: formValues.industry, position: formValues.position, @@ -200,20 +296,41 @@ const ModifyWorkExpirenceModal: FC = (props: Modi resetForm() } - function handleWorkExpirenceEdit(indx: number): void { + async function handleWorkExpirenceEdit(indx: number): Promise { const work: UserTrait = workExpirence ? workExpirence[indx] : {} setEditedItemIndex(indx) + let associatedSkills: any[] = [] + if (work.associatedSkills && Array.isArray(work.associatedSkills) && work.associatedSkills.length > 0) { + try { + const skills = await fetchSkillsByIds( + work.associatedSkills.filter((id): id is string => typeof id === 'string'), + ) + const skillsMap = new Map(skills.map(s => [s.id, s.name])) + + associatedSkills = work.associatedSkills.map((skillId: string) => ({ + id: skillId, + name: skillsMap.get(skillId) || '', + })) + } catch { + associatedSkills = work.associatedSkills.map((skillId: string) => ({ + id: skillId, + name: skillNamesMap[skillId] || '', + })) + } + } + setFormValues({ - city: work.cityTown || work.city, - company: work.company || work.companyName, - currentlyWorking: work.working, + associatedSkills, + company: (work.company || work.companyName || '') as string, + currentlyWorking: work.working || false, + description: work.description || '', endDate: work.timePeriodTo ? new Date(work.timePeriodTo) : (work.endDate ? new Date(work.endDate) : undefined), - industry: work.industry, - position: work.position, + industry: work.industry || '', + position: (work.position || '') as string, startDate: work.timePeriodFrom ? new Date(work.timePeriodFrom) : (work.startDate ? new Date(work.startDate) : undefined), @@ -241,6 +358,7 @@ const ModifyWorkExpirenceModal: FC = (props: Modi } } + console.log(formValues, 'formValues') return ( = (props: Modi className={styles.workExpirenceCardWrap} key={uniqueKey || `${work.position}-${indx}`} > - +
) } diff --git a/src/libs/shared/lib/services/standard-skills/standard-skills.service.ts b/src/libs/shared/lib/services/standard-skills/standard-skills.service.ts index d7c225254..bad3ebea7 100644 --- a/src/libs/shared/lib/services/standard-skills/standard-skills.service.ts +++ b/src/libs/shared/lib/services/standard-skills/standard-skills.service.ts @@ -43,3 +43,23 @@ export async function updateMemberSkills( skills, }) } + +/** + * Fetch skills by their IDs + * @param skillIds Array of skill UUIDs + * @returns Promise with array of UserSkill objects + */ +export async function fetchSkillsByIds(skillIds: string[]): Promise { + if (!skillIds || skillIds.length === 0) { + return Promise.resolve([]) + } + + try { + const skillPromises = skillIds.map(skillId => xhrGetAsync(`${baseUrl}/skills/${skillId}`) + .catch(() => undefined)) + const results = await Promise.all(skillPromises) + return results.filter((skill): skill is UserSkill => (skill !== null || skill !== undefined)) + } catch { + return [] + } +} From 17c42577c062bb781a425c240dfc4c4f1b54c308 Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Thu, 15 Jan 2026 17:57:39 +0530 Subject: [PATCH 2/4] removed console log --- .../ModifyWorkExpirenceModal/ModifyWorkExpirenceModal.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/apps/profiles/src/member-profile/work-expirence/ModifyWorkExpirenceModal/ModifyWorkExpirenceModal.tsx b/src/apps/profiles/src/member-profile/work-expirence/ModifyWorkExpirenceModal/ModifyWorkExpirenceModal.tsx index a15fd5098..a23d08e6c 100644 --- a/src/apps/profiles/src/member-profile/work-expirence/ModifyWorkExpirenceModal/ModifyWorkExpirenceModal.tsx +++ b/src/apps/profiles/src/member-profile/work-expirence/ModifyWorkExpirenceModal/ModifyWorkExpirenceModal.tsx @@ -358,7 +358,6 @@ const ModifyWorkExpirenceModal: FC = (props: Modi } } - console.log(formValues, 'formValues') return ( Date: Thu, 15 Jan 2026 18:35:08 +0530 Subject: [PATCH 3/4] fix: review comments --- .../work-expirence/WorkExpirence.tsx | 98 ++++++++----------- .../standard-skills.service.ts | 44 ++++++++- 2 files changed, 80 insertions(+), 62 deletions(-) diff --git a/src/apps/profiles/src/member-profile/work-expirence/WorkExpirence.tsx b/src/apps/profiles/src/member-profile/work-expirence/WorkExpirence.tsx index 717c68667..e5a943d52 100644 --- a/src/apps/profiles/src/member-profile/work-expirence/WorkExpirence.tsx +++ b/src/apps/profiles/src/member-profile/work-expirence/WorkExpirence.tsx @@ -1,8 +1,8 @@ -import { Dispatch, FC, SetStateAction, useEffect, useMemo, useRef, useState } from 'react' +import { Dispatch, FC, SetStateAction, useCallback, useEffect, useMemo, useState } from 'react' import { useSearchParams } from 'react-router-dom' import { MemberTraitsAPI, useMemberTraits, UserProfile, UserTrait, UserTraitIds } from '~/libs/core' -import { fetchSkillsByIds } from '~/libs/shared/lib/services/standard-skills' +import { useSkillsByIds } from '~/libs/shared/lib/services/standard-skills' import { EDIT_MODE_QUERY_PARAM, profileEditModes } from '../../config' import { AddButton, EditMemberPropertyBtn, EmptySection } from '../../components' @@ -32,75 +32,57 @@ const WorkExpirence: FC = (props: WorkExpirenceProps) => { const workExpirence: UserTrait[] | undefined = useMemo(() => memberWorkExpirenceTraits?.[0]?.traits?.data, [memberWorkExpirenceTraits]) - const [skillNamesMap, setSkillNamesMap] = useState>({}) - const [loadingSkills, setLoadingSkills] = useState(false) - const fetchedSkillIdsRef = useRef>(new Set()) - - useEffect(() => { + // Collect all unique skill IDs from work experience entries + const allSkillIds = useMemo(() => { if (!workExpirence) { - setLoadingSkills(false) - return + return [] } - const allSkillIds = new Set() + const skillIdsSet = new Set() workExpirence.forEach((work: UserTrait) => { if (work.associatedSkills && Array.isArray(work.associatedSkills)) { work.associatedSkills.forEach((skillId: string) => { if (skillId && typeof skillId === 'string') { - allSkillIds.add(skillId) + skillIdsSet.add(skillId) } }) } }) - if (allSkillIds.size > 0) { - const skillIdsToFetch = Array.from(allSkillIds) - .filter(id => !fetchedSkillIdsRef.current.has(id)) - - if (skillIdsToFetch.length > 0) { - setLoadingSkills(true) - skillIdsToFetch.forEach(id => fetchedSkillIdsRef.current.add(id)) - - fetchSkillsByIds(skillIdsToFetch) - .then(skills => { - setSkillNamesMap(prevMap => { - const newMap: Record = { ...prevMap } - skills.forEach(skill => { - if (skill.id && skill.name) { - newMap[skill.id] = skill.name - } - }) - skillIdsToFetch.forEach(skillId => { - if (!newMap[skillId]) { - newMap[skillId] = skillId - } - }) - return newMap - }) - }) - .catch(() => { - setSkillNamesMap(prevMap => { - const fallbackMap: Record = { ...prevMap } - skillIdsToFetch.forEach(skillId => { - if (!fallbackMap[skillId]) { - fallbackMap[skillId] = skillId - } - }) - return fallbackMap - }) - }) - .finally(() => { - setLoadingSkills(false) - }) - } else { - setLoadingSkills(false) - } - } else { - setLoadingSkills(false) - } + return Array.from(skillIdsSet) }, [workExpirence]) - const areSkillsLoaded = (work: UserTrait): boolean => { + // Fetch skills using SWR hook + const { data: fetchedSkills, error: skillsError } = useSkillsByIds( + allSkillIds.length > 0 ? allSkillIds : undefined, + ) + + // Determine loading state: data is undefined and no error yet + const loadingSkills = fetchedSkills === undefined && !skillsError + + // Build skill names map from fetched skills + const skillNamesMap = useMemo(() => { + const map: Record = {} + + if (fetchedSkills) { + fetchedSkills.forEach(skill => { + if (skill.id && skill.name) { + map[skill.id] = skill.name + } + }) + } + + // For skills that weren't found, use ID as fallback + allSkillIds.forEach(skillId => { + if (!map[skillId]) { + map[skillId] = skillId + } + }) + + return map + }, [fetchedSkills, allSkillIds]) + + const areSkillsLoaded = useCallback((work: UserTrait): boolean => { if (!work.associatedSkills || !Array.isArray(work.associatedSkills) || work.associatedSkills.length === 0) { return true } @@ -109,7 +91,7 @@ const WorkExpirence: FC = (props: WorkExpirenceProps) => { const skillName = skillNamesMap[skillId] return skillName && skillName !== skillId }) - } + }, [skillNamesMap]) useEffect(() => { if (props.authProfile && editMode === profileEditModes.workExperience) { diff --git a/src/libs/shared/lib/services/standard-skills/standard-skills.service.ts b/src/libs/shared/lib/services/standard-skills/standard-skills.service.ts index bad3ebea7..603ad7ac2 100644 --- a/src/libs/shared/lib/services/standard-skills/standard-skills.service.ts +++ b/src/libs/shared/lib/services/standard-skills/standard-skills.service.ts @@ -1,3 +1,7 @@ +import { useMemo } from 'react' +import { SWRResponse } from 'swr' +import useSWR from 'swr' + import { EnvironmentConfig } from '~/config' import { UserSkill, xhrGetAsync, xhrPostAsync, xhrPutAsync } from '~/libs/core' @@ -45,21 +49,53 @@ export async function updateMemberSkills( } /** - * Fetch skills by their IDs + * Fetcher function for useSWR to fetch skills by their IDs * @param skillIds Array of skill UUIDs * @returns Promise with array of UserSkill objects */ -export async function fetchSkillsByIds(skillIds: string[]): Promise { +async function fetchSkillsByIdsFetcher(skillIds: string[]): Promise { if (!skillIds || skillIds.length === 0) { - return Promise.resolve([]) + return [] } try { const skillPromises = skillIds.map(skillId => xhrGetAsync(`${baseUrl}/skills/${skillId}`) .catch(() => undefined)) const results = await Promise.all(skillPromises) - return results.filter((skill): skill is UserSkill => (skill !== null || skill !== undefined)) + return results.filter((skill): skill is UserSkill => skill !== null && skill !== undefined) } catch { return [] } } + +/** + * Hook to fetch skills by their IDs using SWR + * @param skillIds Array of skill UUIDs + * @returns SWRResponse with array of UserSkill objects + */ +export function useSkillsByIds(skillIds: string[] | undefined): SWRResponse { + const swrKey = useMemo(() => { + if (!skillIds || skillIds.length === 0) { + return null + } + return ['skills-by-ids', [...skillIds].sort().join(',')] + }, [skillIds]) + + return useSWR( + swrKey, + () => fetchSkillsByIdsFetcher(skillIds!), + { + revalidateOnFocus: false, + revalidateOnReconnect: false, + }, + ) +} + +/** + * Fetch skills by their IDs (legacy async function for backward compatibility) + * @param skillIds Array of skill UUIDs + * @returns Promise with array of UserSkill objects + */ +export async function fetchSkillsByIds(skillIds: string[]): Promise { + return fetchSkillsByIdsFetcher(skillIds) +} From 64edac4aa1bc07f2a80243f62638a1b09f550e7e Mon Sep 17 00:00:00 2001 From: Hentry Martin Date: Thu, 15 Jan 2026 18:41:49 +0530 Subject: [PATCH 4/4] fix: lint --- .../src/member-profile/work-expirence/WorkExpirence.tsx | 6 +++--- .../services/standard-skills/standard-skills.service.ts | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/apps/profiles/src/member-profile/work-expirence/WorkExpirence.tsx b/src/apps/profiles/src/member-profile/work-expirence/WorkExpirence.tsx index e5a943d52..1f2e6730b 100644 --- a/src/apps/profiles/src/member-profile/work-expirence/WorkExpirence.tsx +++ b/src/apps/profiles/src/member-profile/work-expirence/WorkExpirence.tsx @@ -1,7 +1,8 @@ import { Dispatch, FC, SetStateAction, useCallback, useEffect, useMemo, useState } from 'react' import { useSearchParams } from 'react-router-dom' +import { SWRResponse } from 'swr' -import { MemberTraitsAPI, useMemberTraits, UserProfile, UserTrait, UserTraitIds } from '~/libs/core' +import { MemberTraitsAPI, useMemberTraits, UserProfile, UserSkill, UserTrait, UserTraitIds } from '~/libs/core' import { useSkillsByIds } from '~/libs/shared/lib/services/standard-skills' import { EDIT_MODE_QUERY_PARAM, profileEditModes } from '../../config' @@ -52,8 +53,7 @@ const WorkExpirence: FC = (props: WorkExpirenceProps) => { return Array.from(skillIdsSet) }, [workExpirence]) - // Fetch skills using SWR hook - const { data: fetchedSkills, error: skillsError } = useSkillsByIds( + const { data: fetchedSkills, error: skillsError }: SWRResponse = useSkillsByIds( allSkillIds.length > 0 ? allSkillIds : undefined, ) diff --git a/src/libs/shared/lib/services/standard-skills/standard-skills.service.ts b/src/libs/shared/lib/services/standard-skills/standard-skills.service.ts index 603ad7ac2..346261290 100644 --- a/src/libs/shared/lib/services/standard-skills/standard-skills.service.ts +++ b/src/libs/shared/lib/services/standard-skills/standard-skills.service.ts @@ -1,6 +1,5 @@ import { useMemo } from 'react' -import { SWRResponse } from 'swr' -import useSWR from 'swr' +import useSWR, { SWRResponse } from 'swr' import { EnvironmentConfig } from '~/config' import { UserSkill, xhrGetAsync, xhrPostAsync, xhrPutAsync } from '~/libs/core' @@ -76,9 +75,11 @@ async function fetchSkillsByIdsFetcher(skillIds: string[]): Promise export function useSkillsByIds(skillIds: string[] | undefined): SWRResponse { const swrKey = useMemo(() => { if (!skillIds || skillIds.length === 0) { - return null + return undefined } - return ['skills-by-ids', [...skillIds].sort().join(',')] + + return ['skills-by-ids', [...skillIds].sort() + .join(',')] }, [skillIds]) return useSWR(