From b94b6f3c635188e8b0a89a6dddd35b0fc9bd49bd Mon Sep 17 00:00:00 2001 From: Rodrigo Louro Date: Wed, 29 Apr 2026 11:56:24 +0200 Subject: [PATCH] Fix: show briefed checkmark next to volunteer type tag for accompanying volunteers #385 Fix: show briefed checkmark next to volunteer type tag for accompanying volunteers #385 --- .../AccordionVolunteer.tsx | 19 ++++++++++++++++++- .../ProfileHeader/common/labelMaps.ts | 12 +++++++++++- .../volunteer/VolunteerHeader.tsx | 6 ++++++ .../VolunteerProfile/DisplayFields.tsx | 14 +++++++++++++- .../VolunteerProfile/VolunteerProfile.tsx | 13 ++++++++++++- .../Dashboard/Volunteers/VolunteerCard.tsx | 17 +++++++++++++++-- 6 files changed, 75 insertions(+), 6 deletions(-) diff --git a/src/components/Dashboard/Profile/sections/OpportunityVolunteers/AccordionVolunteer.tsx b/src/components/Dashboard/Profile/sections/OpportunityVolunteers/AccordionVolunteer.tsx index 0b1f75fb..5f0ca73e 100644 --- a/src/components/Dashboard/Profile/sections/OpportunityVolunteers/AccordionVolunteer.tsx +++ b/src/components/Dashboard/Profile/sections/OpportunityVolunteers/AccordionVolunteer.tsx @@ -1,9 +1,16 @@ +import { CheckCircleIcon } from "@phosphor-icons/react"; import { Heading4 } from "@/components/styled/text"; import { defaultAvatarVolunteerProfile } from "@/config/constants"; import { getImageUrl } from "@/utils"; -import { ApiVolunteerOpportunityGet, OpportunityVolunteerStatusType } from "need4deed-sdk"; +import { + ApiVolunteerOpportunityGet, + OpportunityVolunteerStatusType, + VolunteerStateCommunicationType, + VolunteerStateTypeType, +} from "need4deed-sdk"; import { useRouter } from "next/navigation"; import { useTranslation } from "react-i18next"; +import { isBriefedAccompanying } from "../ProfileHeader/common"; import StatusBadge from "../../common/StatusBadge"; import { Accordion } from "../shared/Accordion"; import { getDatePrefixKey } from "../shared/getDatePrefixKey"; @@ -31,6 +38,15 @@ export const AccordionVolunteer = ({ const router = useRouter(); const { volunteerId, name, avatarUrl, engagement, volunteeringType } = volunteer; + // TODO: remove cast once SDK adds statusCommunication to ApiVolunteerOpportunityGet + const { statusCommunication } = volunteer as ApiVolunteerOpportunityGet & { + statusCommunication?: VolunteerStateCommunicationType; + }; + const showBriefedCheck = isBriefedAccompanying( + volunteeringType as unknown as VolunteerStateTypeType, + statusCommunication, + ); + const handleGoToProfile = () => { router.push(`/${i18n.language}/dashboard/volunteers/${volunteerId}`); }; @@ -45,6 +61,7 @@ export const AccordionVolunteer = ({ + {showBriefedCheck && } ); diff --git a/src/components/Dashboard/Profile/sections/ProfileHeader/common/labelMaps.ts b/src/components/Dashboard/Profile/sections/ProfileHeader/common/labelMaps.ts index 2aec2bc1..d96a5bdb 100644 --- a/src/components/Dashboard/Profile/sections/ProfileHeader/common/labelMaps.ts +++ b/src/components/Dashboard/Profile/sections/ProfileHeader/common/labelMaps.ts @@ -1,5 +1,15 @@ import { TFunction } from "i18next"; -import { VolunteerStateTypeType } from "need4deed-sdk"; +import { VolunteerStateCommunicationType, VolunteerStateTypeType } from "need4deed-sdk"; + +const ACCOMPANYING_TYPES = [VolunteerStateTypeType.ACCOMPANYING, VolunteerStateTypeType.REGULAR_ACCOMPANYING]; + +export const isBriefedAccompanying = ( + statusType: VolunteerStateTypeType | undefined, + statusCommunication: VolunteerStateCommunicationType | undefined, +): boolean => + !!statusType && + ACCOMPANYING_TYPES.includes(statusType) && + statusCommunication === VolunteerStateCommunicationType.BRIEFED; export const createVolunteerTypeLabelMap = (t: TFunction): Record => ({ [VolunteerStateTypeType.ACCOMPANYING]: t( diff --git a/src/components/Dashboard/Profile/sections/ProfileHeader/volunteer/VolunteerHeader.tsx b/src/components/Dashboard/Profile/sections/ProfileHeader/volunteer/VolunteerHeader.tsx index eb385222..29898cb0 100644 --- a/src/components/Dashboard/Profile/sections/ProfileHeader/volunteer/VolunteerHeader.tsx +++ b/src/components/Dashboard/Profile/sections/ProfileHeader/volunteer/VolunteerHeader.tsx @@ -1,6 +1,7 @@ "use client"; import { defaultAvatarVolunteerProfile, EMPTY_PLACEHOLDER_VALUE } from "@/config/constants"; import { formatDateTime, getImageUrl } from "@/utils"; +import { CheckCircleIcon } from "@phosphor-icons/react"; import { ApiVolunteerGet, VolunteerStateEngagementType } from "need4deed-sdk"; import Image from "next/image"; import { useTranslation } from "react-i18next"; @@ -9,6 +10,7 @@ import { createVolunteerTypeLabelMap, EditButton, HeaderCard, + isBriefedAccompanying, ReturnDateText, StatusRowField, } from "../common"; @@ -27,6 +29,7 @@ export const VolunteerHeader = ({ volunteer }: Props) => { const engagementLabelMap = createEngagementLabelMap(t); const matchLabelMap = createMatchLabelMap(t); const volunteerTypeLabelMap = createVolunteerTypeLabelMap(t); + const showBriefedCheck = isBriefedAccompanying(volunteer.statusType, volunteer.statusCommunication); const fullName = `${volunteer.person.firstName} ${volunteer.person.lastName}`; const avatarUrl = getImageUrl(volunteer.person.avatarUrl || defaultAvatarVolunteerProfile); @@ -73,6 +76,9 @@ export const VolunteerHeader = ({ volunteer }: Props) => { title={t("dashboard.volunteerProfile.volunteerHeader.volunteerType_title")} status={volunteer.statusType} label={volunteer.statusType ? volunteerTypeLabelMap[volunteer.statusType] : undefined} + extra={ + showBriefedCheck ? : undefined + } /> ); diff --git a/src/components/Dashboard/Profile/sections/VolunteerProfile/DisplayFields.tsx b/src/components/Dashboard/Profile/sections/VolunteerProfile/DisplayFields.tsx index 01841a64..a03998c3 100644 --- a/src/components/Dashboard/Profile/sections/VolunteerProfile/DisplayFields.tsx +++ b/src/components/Dashboard/Profile/sections/VolunteerProfile/DisplayFields.tsx @@ -1,3 +1,4 @@ +import { CheckCircleIcon } from "@phosphor-icons/react"; import { EmptyPlaceholder } from "@/components/core/common/EmptyPlaceholder"; import { Tags } from "@/components/core/common/Tags"; import styled from "styled-components"; @@ -39,6 +40,12 @@ const TagsWrapper = styled.div` gap: var(--spacing-8); `; +const BadgeWithCheck = styled.div` + display: flex; + align-items: center; + gap: var(--spacing-8); +`; + type Props = { mainCommunication: string; languagesToTranslate: string; @@ -46,6 +53,7 @@ type Props = { districts: string; volunteerType: string; volunteerTypeStatus: StatusValue | undefined; + showBriefedCheck?: boolean; activities: string[]; skills: string[]; comments: string | undefined; @@ -59,6 +67,7 @@ export function DisplayFields({ districts, volunteerType, volunteerTypeStatus, + showBriefedCheck, activities, skills, comments, @@ -90,7 +99,10 @@ export function DisplayFields({ {t("dashboard.volunteerProfile.profileSection.volunteerType")} {volunteerType && volunteerTypeStatus ? ( - + + + {showBriefedCheck && } + ) : ( )} diff --git a/src/components/Dashboard/Profile/sections/VolunteerProfile/VolunteerProfile.tsx b/src/components/Dashboard/Profile/sections/VolunteerProfile/VolunteerProfile.tsx index cfe79b33..cfa08ee7 100644 --- a/src/components/Dashboard/Profile/sections/VolunteerProfile/VolunteerProfile.tsx +++ b/src/components/Dashboard/Profile/sections/VolunteerProfile/VolunteerProfile.tsx @@ -2,7 +2,14 @@ import Button from "@/components/core/button/Button/Button"; import { useUpdateVolunteerProfile } from "@/hooks/useUpdateVolunteerProfile"; import { zodResolver } from "@hookform/resolvers/zod"; -import { ApiVolunteerGet, Lang, LangPurpose, VolunteerStateTypeType } from "need4deed-sdk"; +import { + ApiVolunteerGet, + Lang, + LangPurpose, + VolunteerStateCommunicationType, + VolunteerStateTypeType, +} from "need4deed-sdk"; +import { isBriefedAccompanying } from "../ProfileHeader/common"; import { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from "react"; import { useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; @@ -167,6 +174,10 @@ export const VolunteerProfile = forwardRef(function districts={formatLocationsForDisplay(volunteer.locations)} volunteerType={getVolunteerTypeLabel(volunteer.statusType, t)} volunteerTypeStatus={volunteer.statusType} + showBriefedCheck={isBriefedAccompanying( + volunteer.statusType, + volunteer.statusCommunication as VolunteerStateCommunicationType, + )} activities={extractTitles(volunteer.activities)} skills={extractTitles(volunteer.skills)} comments={volunteer.infoAbout} diff --git a/src/components/Dashboard/Volunteers/VolunteerCard.tsx b/src/components/Dashboard/Volunteers/VolunteerCard.tsx index 99c51d24..31f2d209 100644 --- a/src/components/Dashboard/Volunteers/VolunteerCard.tsx +++ b/src/components/Dashboard/Volunteers/VolunteerCard.tsx @@ -1,5 +1,10 @@ -import { CheckIcon, FlagIcon, HourglassIcon, SealCheckIcon, SparkleIcon } from "@phosphor-icons/react"; -import { ApiVolunteerGetList, VolunteerStateEngagementType } from "need4deed-sdk"; +import { CheckCircleIcon, CheckIcon, FlagIcon, HourglassIcon, SealCheckIcon, SparkleIcon } from "@phosphor-icons/react"; +import { + ApiVolunteerGetList, + VolunteerStateCommunicationType, + VolunteerStateEngagementType, + VolunteerStateTypeType, +} from "need4deed-sdk"; import { useRouter } from "next/navigation"; import { JSX } from "react"; import { useTranslation } from "react-i18next"; @@ -11,6 +16,7 @@ import { CirclePic } from "@/components/styled/img"; import { Paragraph } from "@/components/styled/text"; import { defaultAvatarURL } from "@/config/constants"; import { getImageUrl } from "@/utils"; +import { isBriefedAccompanying } from "../Profile/sections/ProfileHeader/common"; import { formatAvailabilityItem } from "../Profile/sections/VolunteerProfile/formatters"; import CardDetail from "./CardDetail"; import { getNormalizedVolunteer, groupLanguagesByProficiency } from "./helpers"; @@ -28,6 +34,12 @@ export function VolunteerCard({ volunteer, opportunityId }: Props) { const { id, name, languages, activities, skills, locations, availability, avatarUrl, statusEngagement, statusType } = getNormalizedVolunteer(volunteer); + // TODO: remove cast once SDK adds statusCommunication to ApiVolunteerGetList + const { statusCommunication } = volunteer as ApiVolunteerGetList & { + statusCommunication?: VolunteerStateCommunicationType; + }; + const showBriefedCheck = isBriefedAccompanying(statusType as VolunteerStateTypeType, statusCommunication); + const groupedLanguages = groupLanguagesByProficiency(languages); const availabilities = availability @@ -71,6 +83,7 @@ export function VolunteerCard({ volunteer, opportunityId }: Props) { {statusType.toUpperCase()} + {showBriefedCheck && } )}