From bd92945e5d3f02f39b2cb806a1e3491270637cef Mon Sep 17 00:00:00 2001 From: nadavosa Date: Tue, 14 Jul 2026 11:19:32 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix:=20wire=20real=20data=20into?= =?UTF-8?q?=20agent=20profile=20Volunteers=20section=20(#777)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VolunteerAgents.tsx never fetched any data — useTabTransitions was called with a hardcoded [], so the section always showed 0 in every tab even when volunteers were genuinely matched via the agent's opportunities. Wires it up to the new GET /agent/:id/volunteer-linked endpoint (need4deed-org/be#759), mirroring OpportunityVolunteers.tsx's existing fetch/tabs/mutation pattern. Also fixes two latent bugs in the never-rendered sibling components: - AccordionVolunteer used statusEngagement/statusType field names that don't exist on ApiVolunteerOpportunityGet (it's engagement/ volunteeringType), and a hardcoded placeholder date. - The "go to profile" link used the opportunity-volunteer match id instead of the volunteer's own id, which would have routed to the wrong profile page. --- .../VolunteerAgents/AccordionVolunteer.tsx | 11 +-- .../VolunteerAgents/VolunteerAgents.tsx | 74 +++++++++++++++++-- .../Profile/sections/VolunteerAgents/types.ts | 7 +- src/hooks/useAgentProfileSections.tsx | 2 +- 4 files changed, 76 insertions(+), 18 deletions(-) diff --git a/src/components/Dashboard/Profile/sections/VolunteerAgents/AccordionVolunteer.tsx b/src/components/Dashboard/Profile/sections/VolunteerAgents/AccordionVolunteer.tsx index 4d0abe30..75a5ee01 100644 --- a/src/components/Dashboard/Profile/sections/VolunteerAgents/AccordionVolunteer.tsx +++ b/src/components/Dashboard/Profile/sections/VolunteerAgents/AccordionVolunteer.tsx @@ -32,7 +32,7 @@ export const AccordionVolunteer = ({ const router = useRouter(); const handleGoToProfile = () => { - router.push(`/${i18n.language}/dashboard/volunteers/${volunteer.id}`); + router.push(`/${i18n.language}/dashboard/volunteers/${volunteer.volunteerId}`); }; const engagementStatusLabels = createEngagementStatusLabelMap(t); @@ -44,11 +44,8 @@ export const AccordionVolunteer = ({ {volunteer?.name} - - + + ); @@ -56,7 +53,7 @@ export const AccordionVolunteer = ({ { +type Props = { agentId: Id }; + +export const VolunteerAgents = ({ agentId }: Props) => { const { t } = useTranslation(); - const { selectedTabIndex, setSelectedTabIndex, tabCounts } = useTabTransitions([]); + const queryKey = ["agent-volunteers", String(agentId)]; + + const { data, isLoading } = useGetQuery({ + queryKey, + apiPath: `${apiPathAgent}/${agentId}/volunteer-linked`, + staleTime: cacheTTL, + enabled: !!agentId, + }); + + const volunteers = useMemo(() => data ?? [], [data]); + + const { mutate: updateStatus } = useUpdateOpportunityVolunteerStatus(queryKey); + const { mutate: deleteLink } = useDeleteOpportunityVolunteer(queryKey); + + const { selectedTabIndex, setSelectedTabIndex, currentTabStatus, tabCounts, visibleItems, setItemStatus } = + useTabTransitions(volunteers); const tabs = TAB_STATUS_ORDER.map((key, index) => ({ label: t(`dashboard.volunteerProfile.opportunitiesSec.tabs.${key}`), count: tabCounts[index], })); + const handleMatch = (m2mId: number) => { + setItemStatus(m2mId, OpportunityVolunteerStatusType.MATCHED); + updateStatus({ m2mId, status: OpportunityVolunteerStatusType.MATCHED }); + }; + + const handleNotAMatch = (m2mId: number) => { + setItemStatus(m2mId, ITEM_STATUS_REMOVED); + deleteLink({ m2mId }); + }; + + const handleMarkAsActive = (m2mId: number) => { + setItemStatus(m2mId, OpportunityVolunteerStatusType.ACTIVE); + updateStatus({ m2mId, status: OpportunityVolunteerStatusType.ACTIVE }); + }; + + const handleMarkAsPast = (m2mId: number) => { + setItemStatus(m2mId, OpportunityVolunteerStatusType.PAST); + updateStatus({ m2mId, status: OpportunityVolunteerStatusType.PAST }); + }; + + if (isLoading) { + return ; + } + return ( - + - {t("dashboard.volunteerProfile.opportunitiesSec.emptyState")} + {visibleItems.length === 0 ? ( + {t("dashboard.volunteerProfile.opportunitiesSec.emptyState")} + ) : ( + visibleItems.map((volunteer) => ( + handleMatch(volunteer.id)} + onNotAMatch={() => handleNotAMatch(volunteer.id)} + onMarkAsActive={() => handleMarkAsActive(volunteer.id)} + onMarkAsPast={() => handleMarkAsPast(volunteer.id)} + /> + )) + )} ); }; diff --git a/src/components/Dashboard/Profile/sections/VolunteerAgents/types.ts b/src/components/Dashboard/Profile/sections/VolunteerAgents/types.ts index e4f01299..43d930dd 100644 --- a/src/components/Dashboard/Profile/sections/VolunteerAgents/types.ts +++ b/src/components/Dashboard/Profile/sections/VolunteerAgents/types.ts @@ -1,6 +1,5 @@ import { - ApiVolunteerGetList, - OpportunityVolunteerStatusType, + ApiVolunteerOpportunityGet, VolunteerStateEngagementType, VolunteerStateMatchType, VolunteerStateTypeType, @@ -8,9 +7,7 @@ import { import { TFunction } from "i18next"; -export type MappedVolunteerAgent = ApiVolunteerGetList & { - status: OpportunityVolunteerStatusType; -}; +export type MappedVolunteerAgent = ApiVolunteerOpportunityGet; export const createEngagementStatusLabelMap = (t: TFunction): Record => ({ [VolunteerStateEngagementType.NEW]: t("dashboard.volunteers.filters.engagement.new"), diff --git a/src/hooks/useAgentProfileSections.tsx b/src/hooks/useAgentProfileSections.tsx index ae6c0d2e..96001b37 100644 --- a/src/hooks/useAgentProfileSections.tsx +++ b/src/hooks/useAgentProfileSections.tsx @@ -60,7 +60,7 @@ export const useAgentProfileSections = (agent: ApiAgentProfileGet | undefined) = { iconName: IconName.UserCheck, title: t("dashboard.volunteers.volunteers"), - subComponent: , + subComponent: , }, { iconName: IconName.ShootingStar,