assigners can assign volunteer to a team

This commit is contained in:
memeriau 2022-04-16 18:30:47 +02:00
parent f0f08a91ed
commit 7c96636477
8 changed files with 145 additions and 6 deletions

View File

@ -5,6 +5,7 @@ import classnames from "classnames"
import { selectVolunteerList } from "../../store/volunteerList"
import { selectTeamList } from "../../store/teamList"
import styles from "./styles.module.scss"
import { useTeamAssign } from "./teamAssign.utils"
const selectTeamsWithVolunteersCandidates = createSelector(
selectVolunteerList,
@ -37,7 +38,13 @@ type PropsDaysDisplay = {
const DaysDisplay: FC<PropsDaysDisplay> = ({ dayWishes }): JSX.Element => (
<span className={styles.daysDisplay}>
{dayWishes.map((day) => (day === "S" || day === "D" ? <strong>{day}</strong> : day))}
{dayWishes.map((day) =>
day === "S" || day === "D" ? (
<strong key={day}>{day}</strong>
) : (
<span key={day}>{day}</span>
)
)}
</span>
)
@ -48,10 +55,14 @@ type Props = {
const TeamWithCandidates: FC<Props> = ({ teamId }): JSX.Element | null => {
const teams = useSelector(selectTeamsWithVolunteersCandidates)
const team = teams.find((t) => t.id === teamId)
const [, saveTeam] = useTeamAssign()
const onTeamSelected = useCallback((volunteerId, selectedTeamId) => {
console.log("select ", volunteerId, selectedTeamId)
}, [])
const onTeamSelected = useCallback(
(volunteerId, selectedTeamId) => {
saveTeam(volunteerId, selectedTeamId)
},
[saveTeam]
)
if (!team) return null

View File

@ -0,0 +1,27 @@
import { shallowEqual, useSelector } from "react-redux"
import { useCallback } from "react"
import { selectUserJwtToken } from "../../store/auth"
import { AppState } from "../../store"
import useAction from "../../utils/useAction"
import { fetchVolunteerTeamAssignSet } from "../../store/volunteerTeamAssignSet"
export const useTeamAssign = (): [any, any] => {
const save = useAction(fetchVolunteerTeamAssignSet)
const jwtToken = useSelector(selectUserJwtToken)
const teamSet = useSelector(
(state: AppState) => state.volunteerTeamAssignSet?.entity,
shallowEqual
)
const saveWishes = useCallback(
(volunteerId, teamId) => {
save(jwtToken, 0, {
volunteer: volunteerId,
team: teamId,
})
},
[save, jwtToken]
)
return [teamSet, saveWishes]
}

View File

@ -12,6 +12,7 @@ import {
translationVolunteer,
VolunteerDayWishes,
VolunteerParticipationDetails,
VolunteerTeamAssign,
} from "../../services/volunteers"
import { canonicalEmail } from "../../utils/standardization"
import { getJwt } from "../secure"
@ -246,8 +247,31 @@ export const volunteerParticipationDetailsSet = expressAccessor.set(async (list,
}
})
export const volunteerTeamAssignSet = expressAccessor.set(async (list, body, id) => {
const requestedId = +body[0] || id
const assigner = list.find((v) => v.id === requestedId)
if (!assigner || !assigner.roles.includes("répartiteur")) {
throw Error(`Vous n'avez pas les droits pas assigner les équipes.`)
}
const teamAssign = body[1] as VolunteerTeamAssign
const volunteer = list.find((v) => v.id === teamAssign.volunteer)
if (!volunteer) {
throw Error(`Il n'y a aucun bénévole avec cet identifiant ${teamAssign.volunteer}`)
}
const newVolunteer = _.cloneDeep(volunteer)
newVolunteer.team = teamAssign.team
return {
toDatabase: newVolunteer,
toCaller: {
id: newVolunteer.id,
team: newVolunteer.team,
} as VolunteerTeamAssign,
}
})
function getByEmail<T extends { email: string }>(list: T[], rawEmail: string): T | undefined {
const email = canonicalEmail(rawEmail || "")
const volunteer = list.find((v) => canonicalEmail(v.email) === email)
return volunteer
return list.find((v) => canonicalEmail(v.email) === email)
}

View File

@ -29,6 +29,7 @@ import {
volunteerParticipationDetailsSet,
volunteerTeamWishesSet,
volunteerDayWishesSet,
volunteerTeamAssignSet,
volunteerListGet,
} from "./gsheets/volunteers"
import { wishListGet, wishAdd } from "./gsheets/wishes"
@ -102,6 +103,7 @@ app.post(
)
app.post("/VolunteerDayWishesSet", secure as RequestHandler, volunteerDayWishesSet)
app.post("/VolunteerTeamWishesSet", secure as RequestHandler, volunteerTeamWishesSet)
app.post("/VolunteerTeamAssignSet", secure as RequestHandler, volunteerTeamAssignSet)
// Push notification subscription
app.post("/notifications/subscribe", notificationsSubscribe)

View File

@ -29,6 +29,8 @@ export class Volunteer {
food = ""
team = 0
teamWishes: number[] = []
teamWishesComment = ""
@ -62,6 +64,7 @@ export const translationVolunteer: { [k in keyof Volunteer]: string } = {
tshirtCount: "nbDeTshirts",
tshirtSize: "tailleDeTshirts",
food: "alimentation",
team: "équipe",
teamWishes: "enviesEquipe",
teamWishesComment: "commentaireEnviesEquipe",
hiddenAsks: "questionsCachees",
@ -90,6 +93,7 @@ export const volunteerExample: Volunteer = {
tshirtCount: "1",
tshirtSize: "Femme M",
food: "Végétarien",
team: 2,
teamWishes: [],
teamWishesComment: "",
hiddenAsks: [],
@ -146,3 +150,9 @@ export interface VolunteerParticipationDetails {
adult: Volunteer["adult"]
food: Volunteer["food"]
}
export interface VolunteerTeamAssign {
id: Volunteer["id"]
volunteer: number
team: Volunteer["team"]
}

View File

@ -6,6 +6,7 @@ import {
VolunteerAsks,
VolunteerParticipationDetails,
VolunteerTeamWishes,
VolunteerTeamAssign,
VolunteerWithoutId,
} from "./volunteers"
@ -34,3 +35,6 @@ export const volunteerParticipationDetailsSet =
serviceAccessors.securedCustomPost<[number, Partial<VolunteerParticipationDetails>]>(
"ParticipationDetailsSet"
)
export const volunteerTeamAssignSet =
serviceAccessors.securedCustomPost<[number, Partial<VolunteerTeamAssign>]>("TeamAssignSet")

View File

@ -18,6 +18,7 @@ import volunteerAsksSet from "./volunteerAsksSet"
import volunteerParticipationDetailsSet from "./volunteerParticipationDetailsSet"
import volunteerDayWishesSet from "./volunteerDayWishesSet"
import volunteerTeamWishesSet from "./volunteerTeamWishesSet"
import volunteerTeamAssignSet from "./volunteerTeamAssignSet"
import wishAdd from "./wishAdd"
import wishList from "./wishList"
@ -41,6 +42,7 @@ export default (history: History) => ({
volunteerParticipationDetailsSet,
volunteerDayWishesSet,
volunteerTeamWishesSet,
volunteerTeamAssignSet,
wishAdd,
wishList,
router: connectRouter(history) as any,

View File

@ -0,0 +1,59 @@
import { PayloadAction, createSlice } from "@reduxjs/toolkit"
import { StateRequest, toastError, elementFetch } from "./utils"
import { VolunteerTeamAssign } from "../services/volunteers"
import { AppThunk, AppState } from "."
import { volunteerTeamAssignSet } from "../services/volunteersAccessors"
type StateVolunteerTeamAssignSet = { entity?: VolunteerTeamAssign } & StateRequest
export const initialState: StateVolunteerTeamAssignSet = {
readyStatus: "idle",
}
const volunteerTeamAssignSetSlice = createSlice({
name: "volunteerTeamAssignSet",
initialState,
reducers: {
getRequesting: (_) => ({
readyStatus: "request",
}),
getSuccess: (_, { payload }: PayloadAction<VolunteerTeamAssign>) => ({
readyStatus: "success",
entity: payload,
}),
getFailure: (_, { payload }: PayloadAction<string>) => ({
readyStatus: "failure",
error: payload,
}),
},
})
export default volunteerTeamAssignSetSlice.reducer
export const { getRequesting, getSuccess, getFailure } = volunteerTeamAssignSetSlice.actions
export const fetchVolunteerTeamAssignSet = elementFetch(
volunteerTeamAssignSet,
getRequesting,
getSuccess,
getFailure,
(error: Error) => toastError(`Erreur lors du chargement des notifications: ${error.message}`)
)
const shouldFetchVolunteerTeamAssignSet = (state: AppState, id: number) =>
state.volunteerTeamAssignSet?.readyStatus !== "success" ||
(state.volunteerTeamAssignSet?.entity && state.volunteerTeamAssignSet?.entity?.id !== id)
export const fetchVolunteerTeamAssignSetIfNeed =
(id = 0, wishes: Partial<VolunteerTeamAssign> = {}): AppThunk =>
(dispatch, getState) => {
let jwt = ""
if (!id) {
;({ jwt, id } = getState().auth)
}
if (shouldFetchVolunteerTeamAssignSet(getState(), id))
return dispatch(fetchVolunteerTeamAssignSet(jwt, id, wishes))
return null
}