Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions public/locales/de/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,13 @@
"weekdays": "Wochentage",
"weekends": "Wochenenden"
}
},
"volunteerType_title": "Freiwilligen-Typ",
"volunteerType_options": {
"accompanying": "Begleitend",
"regular": "Regelmäßig",
"events": "Veranstaltungen",
"regular-accompanying": "Regelmäßig + Begleitend"
}
},
"volunteers": "Freiwillige",
Expand All @@ -414,6 +421,13 @@
"advanced": "Fortgeschritten",
"fluent": "Fließend",
"native": "Muttersprache"
},
"matchStatus": {
"vol-no-matches": "Keine Zuweisungen",
"vol-pending-match": "Zuordnung ausstehend",
"vol-matched": "Zugeordnet",
"vol-needs-rematch": "Erneute Zuordnung erforderlich",
"vol-past": "Vergangen"
}
},
"home": {
Expand Down
14 changes: 14 additions & 0 deletions public/locales/en/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,13 @@
"weekdays": "Weekdays",
"weekends": "Weekends"
}
},
"volunteerType_title": "Volunteer type",
"volunteerType_options": {
"accompanying": "Accompanying",
"regular": "Regular",
"events": "Events",
"regular-accompanying": "Regular + Accompanying"
}
},
"volunteers": "Volunteers",
Expand All @@ -414,6 +421,13 @@
"advanced": "Advanced",
"fluent": "Fluent",
"native": "Native"
},
"matchStatus": {
"vol-no-matches": "No matches",
"vol-pending-match": "Pending match",
"vol-matched": "Matched",
"vol-needs-rematch": "Needs rematch",
"vol-past": "Past"
}
},
"home": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
ApiVolunteerGetList,
OpportunityVolunteerStatusType,
VolunteerStateEngagementType,
VolunteerStateMatchType,
VolunteerStateTypeType,
} from "need4deed-sdk";

Expand All @@ -20,6 +21,14 @@ export const createEngagementStatusLabelMap = (t: TFunction): Record<VolunteerSt
[VolunteerStateEngagementType.AVAILABLE]: t("dashboard.volunteers.filters.engagement.available"),
});

export const createMatchStatusLabelMap = (t: TFunction): Record<VolunteerStateMatchType, string> => ({
[VolunteerStateMatchType.NO_MATCHES]: t("dashboard.volunteers.matchStatus.vol-no-matches"),
[VolunteerStateMatchType.PENDING_MATCH]: t("dashboard.volunteers.matchStatus.vol-pending-match"),
[VolunteerStateMatchType.MATCHED]: t("dashboard.volunteers.matchStatus.vol-matched"),
[VolunteerStateMatchType.NEEDS_REMATCH]: t("dashboard.volunteers.matchStatus.vol-needs-rematch"),
[VolunteerStateMatchType.PAST]: t("dashboard.volunteers.matchStatus.vol-past"),
});

export const createStatusLabelMap = (t: TFunction): Record<VolunteerStateTypeType, string> => ({
[VolunteerStateTypeType.ACCOMPANYING]: t(
"dashboard.volunteerProfile.volunteerHeader.volunteerType_options.accompanying",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ interface Props {
export default function FiltersContent({ setFilter, filter }: Props) {
const { t } = useTranslation();

const { availabilityFilters, districtFilters, engagementFilters, languageFilters, accompanyingFilter } =
const { availabilityFilters, districtFilters, engagementFilters, languageFilters, accompanyingFilter, typeFilters } =
createFilterItems(filter, setFilter, t);

return (
Expand Down Expand Up @@ -43,6 +43,7 @@ export default function FiltersContent({ setFilter, filter }: Props) {
</AccompanyingFilterHeaderContainer>
</AccompanyingFilterContainer>

<AccordionFilter header={t("dashboard.volunteers.filters.volunteerType_title")} items={typeFilters} />
<AccordionFilter header={t("dashboard.volunteers.filters.engagement.header")} items={engagementFilters} />
<AccordionFilter header={t("dashboard.volunteers.filters.district")} items={districtFilters} />
<AccordionFilter header={t("dashboard.volunteers.filters.languages")} items={languageFilters} />
Expand Down
8 changes: 7 additions & 1 deletion src/components/Dashboard/Volunteers/Filters/constants.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { ByDay, OccasionalType, QueryParamsKeys, TimeSlot, VolunteerStateEngagementType } from "need4deed-sdk";
import { ByDay, OccasionalType, QueryParamsKeys, TimeSlot, VolunteerStateEngagementType, VolunteerStateTypeType } from "need4deed-sdk";
import { CardsFilter } from "./types";

export const defaultVolunteerCardsFilter: CardsFilter = {
[QueryParamsKeys.SEARCH]: "",
[QueryParamsKeys.ACCOMPANYING]: false,
type: {
[VolunteerStateTypeType.ACCOMPANYING]: false,
[VolunteerStateTypeType.REGULAR]: false,
[VolunteerStateTypeType.EVENTS]: false,
[VolunteerStateTypeType.REGULAR_ACCOMPANYING]: false,
},
[QueryParamsKeys.DISTRICT]: {},
[QueryParamsKeys.LANGUAGE]: {},
[QueryParamsKeys.ENGAGEMENT]: {
Expand Down
13 changes: 10 additions & 3 deletions src/components/Dashboard/Volunteers/Filters/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ export const createFilterItems = (filter: CardsFilter, setFilter: SetFilter<Card
t(`dashboard.volunteers.filters.${key}`),
);

const typeFilters = generateNestedFilterControlItems(
filter.type,
setFilter,
"type",
(key) => t(`dashboard.volunteers.filters.volunteerType_options.${key}`),
);

const districtFilters = generateNestedFilterControlItems(
filter[QueryParamsKeys.DISTRICT],
setFilter,
Expand All @@ -35,7 +42,7 @@ export const createFilterItems = (filter: CardsFilter, setFilter: SetFilter<Card

const availabilityFilters = createAvailabilityFilterItems(filter[QueryParamsKeys.AVAILABILITY], setFilter, t);

return { districtFilters, languageFilters, engagementFilters, availabilityFilters, accompanyingFilter };
return { districtFilters, languageFilters, engagementFilters, availabilityFilters, accompanyingFilter, typeFilters };
};

/**
Expand Down Expand Up @@ -77,10 +84,10 @@ export const createSelectedFilterItemsAsFlatArray = (
) => {
const filterItems = createFilterItems(filter, setFilter, t);

const { districtFilters, engagementFilters, languageFilters, availabilityFilters, accompanyingFilter } = filterItems;
const { districtFilters, engagementFilters, languageFilters, availabilityFilters, accompanyingFilter, typeFilters } = filterItems;
const flatAvFilters = availabilityFilters.map((avFilter) => avFilter.items).flat();

return [accompanyingFilter, ...districtFilters, ...engagementFilters, ...languageFilters, ...flatAvFilters].filter(
return [accompanyingFilter, ...typeFilters, ...districtFilters, ...engagementFilters, ...languageFilters, ...flatAvFilters].filter(
(f) => f.checked,
);
};
1 change: 1 addition & 0 deletions src/components/Dashboard/Volunteers/Filters/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface CardsFilter {
[QueryParamsKeys.AVAILABILITY]: Availability;
[QueryParamsKeys.DISTRICT]: SelectionMap;
[QueryParamsKeys.LANGUAGE]: SelectionMap;
type: SelectionMap;
}

export type CardFilterKeys = keyof CardsFilter;
28 changes: 26 additions & 2 deletions src/components/Dashboard/Volunteers/VolunteerCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
ApiVolunteerGetList,
VolunteerStateCommunicationType,
VolunteerStateEngagementType,
VolunteerStateMatchType,
VolunteerStateTypeType,
} from "need4deed-sdk";
import { useRouter } from "next/navigation";
Expand Down Expand Up @@ -34,10 +35,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 & {
// Cast until SDK PR #99 adds statusCommunication and statusMatch to ApiVolunteerGetList
const { statusCommunication, statusMatch } = volunteer as ApiVolunteerGetList & {
statusCommunication?: VolunteerStateCommunicationType;
statusMatch?: VolunteerStateMatchType;
};

const showBriefedCheck = isBriefedAccompanying(statusType as VolunteerStateTypeType, statusCommunication);

const groupedLanguages = groupLanguagesByProficiency(languages);
Expand Down Expand Up @@ -87,6 +90,19 @@ export function VolunteerCard({ volunteer, opportunityId }: Props) {
</TagDiv>
</>
)}

{statusMatch && (
<StatusDiv>
<Paragraph
fontWeight="var(--dashboard-volunteers-card-status-fontWeight)"
fontSize="var(--dashboard-volunteers-card-status-fontSize)"
lineheight="var(--dashboard-volunteers-card-status-lineHeight)"
color={stateMatchColorMap[statusMatch]}
>
{t(`dashboard.volunteers.matchStatus.${statusMatch}`)}
</Paragraph>
</StatusDiv>
)}
</>
</StatusTagsDiv>

Expand Down Expand Up @@ -141,6 +157,14 @@ export default VolunteerCard;

/* Helper maps */

const stateMatchColorMap: Record<VolunteerStateMatchType, string> = {
[VolunteerStateMatchType.NO_MATCHES]: "var(--color-grey-700)",
[VolunteerStateMatchType.PENDING_MATCH]: "var(--color-blue-700)",
[VolunteerStateMatchType.MATCHED]: "var(--color-green-700)",
[VolunteerStateMatchType.NEEDS_REMATCH]: "var(--color-red-700)",
[VolunteerStateMatchType.PAST]: "var(--color-grey-500)",
};

const stateEngagementColorMap: Record<VolunteerStateEngagementType, string> = {
[VolunteerStateEngagementType.NEW]: "var(--color-red-500)",
[VolunteerStateEngagementType.ACTIVE]: "var(--color-green-700)",
Expand Down
3 changes: 3 additions & 0 deletions src/components/Dashboard/Volunteers/VolunteerTableList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useTranslation } from "react-i18next";
import styled from "styled-components";
import {
createEngagementStatusLabelMap,
createMatchStatusLabelMap,
createStatusLabelMap,
} from "@/components/Dashboard/Profile/sections/VolunteerAgents/types";
import { Table, TableBody, TableContainer, TableHeader, TableHeaderCell } from "@/components/core/common/Table";
Expand Down Expand Up @@ -34,6 +35,7 @@ export function VolunteerTableList({

const engagementLabels = useMemo(() => createEngagementStatusLabelMap(t), [t]);
const typeLabels = useMemo(() => createStatusLabelMap(t), [t]);
const matchLabels = useMemo(() => createMatchStatusLabelMap(t), [t]);

const goToPage = (page: number) => {
if (page > 0 && page <= totalPages) setCurrentPage(page);
Expand All @@ -60,6 +62,7 @@ export function VolunteerTableList({
isLast={index === volunteers.length - 1}
engagementLabels={engagementLabels}
typeLabels={typeLabels}
matchLabels={matchLabels}
opportunityId={opportunityId}
/>
))}
Expand Down
10 changes: 7 additions & 3 deletions src/components/Dashboard/Volunteers/VolunteerTableRow.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
"use client";

import { ApiVolunteerGetList } from "need4deed-sdk";
import { ApiVolunteerGetList, VolunteerStateMatchType } from "need4deed-sdk";
import { useRouter } from "next/navigation";
import { useTranslation } from "react-i18next";
import styled from "styled-components";
import type {
createEngagementStatusLabelMap,
createMatchStatusLabelMap,
createStatusLabelMap,
} from "@/components/Dashboard/Profile/sections/VolunteerAgents/types";
import { TableCell, TableRow } from "@/components/core/common/Table";
Expand All @@ -18,14 +19,17 @@ interface TableRowProps {
isLast: boolean;
engagementLabels: ReturnType<typeof createEngagementStatusLabelMap>;
typeLabels: ReturnType<typeof createStatusLabelMap>;
matchLabels: ReturnType<typeof createMatchStatusLabelMap>;
opportunityId?: string;
}

export function VolunteerTableRow({ volunteer, isLast, engagementLabels, typeLabels, opportunityId }: TableRowProps) {
export function VolunteerTableRow({ volunteer, isLast, engagementLabels, typeLabels, matchLabels, opportunityId }: TableRowProps) {
const { i18n } = useTranslation();
const router = useRouter();

const { id, name, avatarUrl, statusEngagement, statusType, languages, locations } = volunteer;
// Cast until SDK PR #99 adds statusMatch to ApiVolunteerGetList
const { statusMatch } = volunteer as ApiVolunteerGetList & { statusMatch?: VolunteerStateMatchType };

const languageText =
languages
Expand Down Expand Up @@ -57,7 +61,7 @@ export function VolunteerTableRow({ volunteer, isLast, engagementLabels, typeLab
{statusEngagement ? engagementLabels[statusEngagement] : "—"}
</TableCell>
<TableCell $width="140px" data-testid={`volunteer-match-${id}`}>
{statusMatch ? matchLabels[statusMatch] : "—"}
</TableCell>
<TableCell $width="180px" data-testid={`volunteer-language-${id}`}>
{languageText}
Expand Down
12 changes: 12 additions & 0 deletions src/components/Dashboard/Volunteers/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ export function serializeFilters(
if (filter.accompanying) params.set(QueryParamsKeys.ACCOMPANYING, "true");
else params.delete(QueryParamsKeys.ACCOMPANYING);

params.delete("type");
Object.entries(filter.type).forEach(([key, value]) => {
if (value === true) params.append("type", key);
});

// 2. Clear all existing 'district' params
params.delete(QueryParamsKeys.DISTRICT);
Object.entries(filter.district).forEach(([key, value]) => {
Expand Down Expand Up @@ -144,6 +149,13 @@ export function deserializeVolunteerFilters(filter: CardsFilter, searchParams: R
newFilter.accompanying = true;
}

const queryTypes = searchParams.getAll("type");
queryTypes.forEach((t) => {
if (newFilter.type[t] !== undefined) {
newFilter.type[t] = true;
}
});

const queryDistricts = searchParams.getAll(QueryParamsKeys.DISTRICT);
queryDistricts.forEach((d) => {
// Check if the query param value is exist in the filters. if not, ignore that query param !!!
Expand Down
Loading