Add Meals form and ask; Add disabling registration

This commit is contained in:
pikiou 2022-06-10 11:51:00 +02:00
parent fac06c1b41
commit ff05fed345
19 changed files with 672 additions and 20 deletions

View File

@ -0,0 +1,38 @@
import { get } from "lodash"
import { useCallback } from "react"
import { fetchVolunteerAsksSet } from "../../store/volunteerAsksSet"
import { useAskTools, addAsk, answerLaterOnProfile } from "./utils"
import MealsForm, { fetchFor as fetchForMealsForm } from "../VolunteerBoard/MealsForm/MealsForm"
import { useUserMeals } from "../VolunteerBoard/meals.utils"
export function AskMeals(asks: JSX.Element[], id: number): void {
const { dispatch, jwtToken, volunteerAsks } = useAskTools()
const onSubmit = useCallback((): void => {
dispatch(
fetchVolunteerAsksSet(jwtToken, 0, {
hiddenAsks: [...(volunteerAsks?.hiddenAsks || []), id],
})
)
}, [dispatch, id, jwtToken, volunteerAsks?.hiddenAsks])
const [meals] = useUserMeals()
const needsMeals = get(meals, "needsMeals", false)
const canHostCount = get(meals, "canHostCount", 0)
const distanceToFestival = get(meals, "distanceToFestival", 0)
const mealsComment = get(meals, "mealsComment", "")
const needToShow =
!needsMeals && canHostCount === 0 && distanceToFestival === 0 && mealsComment === ""
addAsk(
asks,
id,
volunteerAsks,
false,
needToShow,
<MealsForm afterSubmit={onSubmit}>{answerLaterOnProfile}</MealsForm>
)
}
// Fetch server-side data here
export const fetchFor = [...fetchForMealsForm]

View File

@ -6,6 +6,7 @@ import { AskWelcome } from "./AskWelcome"
import { AskDiscord, fetchFor as fetchForDiscord } from "./AskDiscord"
import { AskDayWishes, fetchFor as fetchForDayWishes } from "./AskDayWishes"
import { AskHosting, fetchFor as fetchForHosting } from "./AskHosting"
import { AskMeals, fetchFor as fetchForMeals } from "./AskMeals"
import { AskTeamWishes, fetchFor as fetchForTeamWishes } from "./AskTeamWishes"
import {
AskParticipationDetails,
@ -24,6 +25,7 @@ const Asks = (): JSX.Element | null => {
AskTeamWishes(asks, 11)
AskParticipationDetails(asks, 12)
AskHosting(asks, 20)
AskMeals(asks, 22)
AskPushNotif(asks, 99)
@ -61,6 +63,7 @@ export const fetchFor = [
...fetchForDiscord,
...fetchForDayWishes,
...fetchForHosting,
...fetchForMeals,
...fetchForTeamWishes,
...fetchForParticipationDetails,
]

View File

@ -55,6 +55,8 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
const meetingDateList = useSelector(selectMiscMeetingDateList)
const enableRegistering = false
useEffect(() => {
const timer = setInterval(() => {
setChangingBackground((changingBackground + 1) % animations.length)
@ -234,20 +236,33 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
</p>
<div className={styles.beneImg}> </div>
</dd>
{!enableRegistering && (
<dt>
L'inscription est clôturée pour l'édition 2022, mais si l'expérience vous tente,
remplissez le formulaire suivant pour devenir bénévole à PeL 2023 !<br />
Dès septembre on se rencontrera sur Paris en petits groupes pour discuter du
festival, du bénévolat et surtout faire connaissance :)
<br />
</dt>
)}
{enableRegistering && (
<dt>
Si l&apos;expérience vous tente, remplissez le formulaire suivant pour devenir
bénévole !<br />
Vous pouvez aussi juste nous rencontrer avant de vous décider à devenir bénévole, on
comprend qu&apos;un saut pareil dans l&apos;inconnu soit difficile.
Vous pouvez aussi juste nous rencontrer avant de vous décider à devenir
bénévole, on comprend qu&apos;un saut pareil dans l&apos;inconnu soit difficile.
<br />
Dans les deux cas, venez rencontrer une poignée d'entre nous dans un bar/resto près
de Châtelet ! :) Sur inscription uniquement...
Dans les deux cas, venez rencontrer une poignée d'entre nous dans un bar/resto
près de Châtelet ! :) Sur inscription uniquement...
<br />
</dt>
)}
</dl>
)
const potentialVolunteerQuestion = (
const potentialVolunteerQuestion = enableRegistering && (
<div className={styles.inputWrapper}>
<div className={styles.leftCol}>
<div className={styles.multipleChoiceTitle}>Je veux devenir bénévole :</div>
@ -431,7 +446,7 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
</>
)
const meeting = (
const meeting = enableRegistering && (
<>
<dl className={styles.registerIntro}>
{!potentialVolunteer && <dt>Faisons connaissance !</dt>}
@ -535,7 +550,7 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
</>
)
const helpBefore = !potentialVolunteer && (
const helpBefore = enableRegistering && !potentialVolunteer && (
<>
<dl className={styles.registerIntro}>
<dt>Bénévolat en amont du festival</dt>
@ -608,7 +623,7 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
</>
)
const pelMemberQuestion = !potentialVolunteer && (
const pelMemberQuestion = enableRegistering && !potentialVolunteer && (
<>
<dl className={styles.registerIntro}>
<dt>Association Paris est Ludique</dt>
@ -784,7 +799,7 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
{(potentialVolunteer || !alreadyVolunteer) && (
<>
{commentQuestion}
{enableRegistering && commentQuestion}
{cameAsVisitor}
{meeting}
{helpBefore}
@ -792,6 +807,7 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
{(potentialVolunteer || pelMember) && (
<>
{nameMobileEmail}
{!enableRegistering && commentQuestion}
{howToContact !== "Aucun" && submitButton}
</>
)}

View File

@ -3,6 +3,8 @@ import DayWishes from "./DayWishes/DayWishes"
import DayWishesFormModal from "./DayWishesForm/DayWishesFormModal"
import Hosting from "./Hosting/Hosting"
import HostingFormModal from "./HostingForm/HostingFormModal"
import Meals from "./Meals/Meals"
import MealsFormModal from "./MealsForm/MealsFormModal"
import ParticipationDetails from "./ParticipationDetails/ParticipationDetails"
import ParticipationDetailsFormModal from "./ParticipationDetailsForm/ParticipationDetailsFormModal"
import TeamWishes from "./TeamWishes/TeamWishes"
@ -11,6 +13,7 @@ import withUserConnected from "../../utils/withUserConnected"
import ContentTitle from "../ui/Content/ContentTitle"
import { fetchFor as fetchForDayWishesForm } from "./DayWishesForm/DayWishesForm"
import { fetchFor as fetchForHostingForm } from "./HostingForm/HostingForm"
import { fetchFor as fetchForMealsForm } from "./MealsForm/MealsForm"
import { fetchFor as fetchForParticipationDetailsForm } from "./ParticipationDetailsForm/ParticipationDetailsForm"
import { fetchFor as fetchForTeamWishesForm } from "./TeamWishesForm/TeamWishesForm"
import VolunteerTeam from "./VolunteerTeam/VolunteerTeam"
@ -27,6 +30,8 @@ const Board: FC = (): JSX.Element => (
<VolunteerTeam />
<Hosting />
<HostingFormModal />
<Meals />
<MealsFormModal />
</>
)
@ -35,6 +40,7 @@ export default memo(withUserConnected(Board))
export const fetchFor = [
...fetchForDayWishesForm,
...fetchForHostingForm,
...fetchForMealsForm,
...fetchForParticipationDetailsForm,
...fetchForTeamWishesForm,
]

View File

@ -0,0 +1,65 @@
import { FC, memo, useCallback } from "react"
import { find, get } from "lodash"
import styles from "./styles.module.scss"
import { useUserMeals, mealDays, MealOption } from "../meals.utils"
import useAction from "../../../utils/useAction"
import { displayModal, MODAL_IDS } from "../../../store/ui"
import { useUserDayWishes } from "../daysWishes.utils"
const Meals: FC = (): JSX.Element | null => {
const [userMeals] = useUserMeals()
const [userWishes] = useUserDayWishes()
const meals = get(userMeals, "meals", [])
const dayWishesString = get(userWishes, "dayWishes", [])
const execDisplayModal = useAction(displayModal)
const onEdit = useCallback(() => execDisplayModal(MODAL_IDS.MEALS), [execDisplayModal])
const mealChoices = mealDays.map((meal, i: number) =>
find(meal.options, { abbr: meals[i] || "" })
) as MealOption[]
function getMealElement(i: number): JSX.Element {
const mealChoice = mealChoices[i]
return (
<div className={styles.mealsLabel} key={mealDays[i].name}>
{mealDays[i].name}, {mealChoice.title}
</div>
)
}
return (
<div className={styles.meals}>
<div className={styles.title}>Mes repas</div>
{dayWishesString.includes("S") ? (
<>
{getMealElement(0)}
<div className={styles.mealsLabel} key="SamediSoir">
Samedi soir, apéro dînatoire
</div>
</>
) : (
<div className={styles.mealsLabel} key="Samedi">
Non bénévole le samedi
</div>
)}
{dayWishesString.includes("D") ? (
<>
{getMealElement(1)}
{getMealElement(2)}
</>
) : (
<div className={styles.mealsLabel} key="Dimanche">
Non bénévole le dimanche
</div>
)}
<div className={styles.editButton} key="edit">
<button type="button" onClick={onEdit}>
Modifier
</button>
</div>
</div>
)
}
export default memo(Meals)

View File

@ -0,0 +1,53 @@
@import "../../../theme/variables";
@import "../../../theme/mixins";
.title {
padding-bottom: 10px;
font-weight: bold;
}
.mealsLabel {
margin-right: 5px;
font-style: bold;
}
.meals {
@include inner-content-wrapper();
position: relative;
padding-right: 90px;
}
.mealsLabel,
.commentLine {
margin-bottom: 5px;
span {
display: inline-block;
}
}
.lineEmpty {
color: $color-red;
font-style: italic;
}
.commentLineTitle {
padding-right: 5px;
}
.commentLineText {
font-style: italic;
}
.editButton {
@include vertical-center();
position: absolute;
right: 20px;
button {
color: $color-green;
font-weight: bold;
cursor: pointer;
}
}

View File

@ -0,0 +1,152 @@
import { get } from "lodash"
import { FC, memo, ReactNode, useCallback, useEffect, useState } from "react"
import classnames from "classnames"
import styles from "./styles.module.scss"
import { mealDays, MealOption, useUserMeals } from "../meals.utils"
import FormButton from "../../Form/FormButton/FormButton"
import { fetchVolunteerMealsSetIfNeed } from "../../../store/volunteerMealsSet"
import IgnoreButton from "../../Form/IgnoreButton/IgnoreButton"
import { useUserDayWishes } from "../daysWishes.utils"
import { fetchVolunteerDayWishesSetIfNeed } from "../../../store/volunteerDayWishesSet"
type Props = {
children?: ReactNode | undefined
afterSubmit?: () => void | undefined
}
const MealsForm: FC<Props> = ({ children, afterSubmit }): JSX.Element => {
const [saturdayLunchMeal, setSaturdayLunchMeal] = useState("")
const [sundayLunchMeal, setSundayLunchMeal] = useState("")
const [sundayDinnerMeal, setSundayDinnerMeal] = useState("")
const [userMeals, saveMeals] = useUserMeals()
const [userWishes] = useUserDayWishes()
const meals = get(userMeals, "meals", [])
const dayWishesString = get(userWishes, "dayWishes", [])
useEffect(() => {
setSaturdayLunchMeal(meals[0] || "")
setSundayLunchMeal(meals[1] || "")
setSundayDinnerMeal(meals[2] || "")
}, [meals])
const onChoiceSubmit = useCallback(() => {
saveMeals([saturdayLunchMeal, sundayLunchMeal, sundayDinnerMeal])
if (afterSubmit) afterSubmit()
}, [saveMeals, saturdayLunchMeal, sundayLunchMeal, sundayDinnerMeal, afterSubmit])
const getBreakfeastElement = (dayName: string): JSX.Element => (
<div key={`${dayName}Matin`}>
<b>{dayName} matin</b>, petit dej à 8h avec jus de pomme, multifruit, café, brioches et
quatre-quarts, crêpes si bénévoles motivés.
</div>
)
const getMealElement = (i: number, meal: string, setMeal: (m: string) => void): JSX.Element => (
<div className={classnames(styles.inputWrapper, styles.noBottomMargin)}>
<div className={styles.leftCol}>
<div className={styles.needsMealsTitle}>
<b>{mealDays[i].name}</b>
{i === 0 && <>, accompagné d'un délicieux brownie tout chocolat</>}
{i === 1 && (
<>, accompagné du même brownie. Enfin, un autre que celui de la veille</>
)}{" "}
:
</div>
</div>
<div className={styles.rightCol}>
{mealDays[i].options.map((option: MealOption) => (
<label className={styles.mealsLabel} key={option.abbr}>
<input
type="radio"
name={`meal${i}`}
onChange={() => setMeal(option.abbr)}
checked={meal === option.abbr}
/>{" "}
{option.title}
</label>
))}
</div>
</div>
)
const getNotVolunteerElement = (dayName: string): JSX.Element => (
<div key={dayName}>
<b>{dayName} matin/midi/soir</b>, comme tu n'es pas bénévole ce jour-, on a rien
prévu. Mais{" "}
<a href="mailto:benevoles@parisestludique.fr" target="_blank" rel="noreferrer">
contacte-nous
</a>{" "}
si tu veux qu'on te prévoie un dîner.{" "}
{dayName === "Dimanche" && (
<>
Surtout dimanche soir si tu redeviens bénévole pour nous aider à ranger un peu
le festival !
</>
)}
</div>
)
return (
<div>
<div className={styles.title}>Mes repas</div>
{dayWishesString.includes("S") ? (
<>
{getBreakfeastElement("Samedi")}
{getMealElement(0, saturdayLunchMeal, setSaturdayLunchMeal)}
<div className={styles.mealsLabel} key="SamediSoir">
<b>Samedi soir</b>, apéro dînatoire
</div>
</>
) : (
getNotVolunteerElement("Samedi")
)}
<br />
<br />
<br />
{dayWishesString.includes("D") ? (
<>
{getBreakfeastElement("Dimanche")}
{getMealElement(1, sundayLunchMeal, setSundayLunchMeal)}
{getMealElement(2, sundayDinnerMeal, setSundayDinnerMeal)}
</>
) : (
getNotVolunteerElement("Dimanche")
)}
<div className={classnames(styles.inputWrapper)}>
<div>
<b>Jeudi, vendredi et lundi</b>, chacun improvise. Peut-être resto à proximité.
</div>
</div>
<div className={styles.buttonWrapper}>
<FormButton onClick={onChoiceSubmit}>Enregistrer</FormButton>
{children === undefined && (
<>
{" "}
<FormButton onClick={afterSubmit} type="grey">
Annuler
</FormButton>{" "}
</>
)}
{children !== undefined && (
<>
{" "}
<IgnoreButton onClick={afterSubmit} text="Ignorer">
{children}
</IgnoreButton>{" "}
</>
)}
</div>
</div>
)
}
MealsForm.defaultProps = {
children: undefined,
afterSubmit: undefined,
}
export default memo(MealsForm)
// Fetch server-side data here
export const fetchFor = [fetchVolunteerMealsSetIfNeed, fetchVolunteerDayWishesSetIfNeed]

View File

@ -0,0 +1,18 @@
import { FC, memo, useCallback } from "react"
import { hideModal, MODAL_IDS } from "../../../store/ui"
import Modal from "../../Modal/Modal"
import useAction from "../../../utils/useAction"
import MealsForm from "./MealsForm"
const MealsFormModal: FC = (): JSX.Element => {
const execHideModal = useAction(hideModal)
const afterFormSubmit = useCallback(() => execHideModal(), [execHideModal])
return (
<Modal modalId={MODAL_IDS.MEALS}>
<MealsForm afterSubmit={afterFormSubmit} />
</Modal>
)
}
export default memo(MealsFormModal)

View File

@ -0,0 +1,122 @@
@import "../../../theme/variables";
@import "../../../theme/mixins";
.title {
padding: 15px 0;
font-weight: bold;
text-align: center;
}
.inputWrapper {
margin: 25px 0;
@include desktop {
display: flex;
}
}
.leftCol {
flex: 0 0 320px;
}
.rightCol {
width: 100%;
text-align: center;
}
.needsMealsTitle {
display: inline-block;
width: 320px;
margin-bottom: 10px;
}
.mealsLabel {
text-align: left;
display: inline-block;
margin-bottom: 10px;
width: 220px;
}
.canHostCountTitle {
display: inline-block;
width: 320px;
margin-bottom: 10px;
}
.canHostCountLabel {
text-align: left;
display: inline-block;
margin-bottom: 10px;
width: 80px;
}
.distanceToFestivalTitle {
display: inline-block;
width: 320px;
margin-bottom: 10px;
}
.distanceToFestivalLabel {
text-align: left;
display: inline-block;
margin-bottom: 10px;
width: 80px;
}
.mealsTitle {
display: inline-block;
width: 320px;
margin-bottom: 10px;
}
.mealsList {
@include clear-ul-style;
display: inline-block;
width: 204px;
text-align: center;
}
.mealsItem {
display: inline-block;
margin: 3px;
}
.mealsButton {
margin: 0;
padding: 7px 2px 6px;
border: 0;
border-radius: 0;
width: 90px;
text-align: center;
color: $color-grey-dark;
background-color: $color-grey-light;
cursor: pointer;
&.active {
color: $color-yellow;
background-color: $color-black;
}
}
.mealsCommentWrapper {
margin: 6px 0 14px;
label {
display: block;
padding: 6px 0 2px 4px;
}
textarea {
width: 100%;
height: 50px;
padding: 5px;
border: 1px solid $color-grey-light;
background-color: $color-grey-lighter;
outline: 0;
}
}
.buttonWrapper {
margin-bottom: 10px;
text-align: center;
}

View File

@ -4,8 +4,16 @@ import { selectUserJwtToken } from "../../store/auth"
import { AppState } from "../../store"
import { fetchVolunteerHostingSet } from "../../store/volunteerHostingSet"
import useAction from "../../utils/useAction"
import { VolunteerHosting } from "../../services/volunteers"
export const useUserHosting = (): [any, any] => {
type SetFunction = (
needsHosting: VolunteerHosting["needsHosting"],
canHostCount: VolunteerHosting["canHostCount"],
distanceToFestival: VolunteerHosting["distanceToFestival"],
hostingComment: VolunteerHosting["hostingComment"]
) => void
export const useUserHosting = (): [VolunteerHosting | undefined, SetFunction] => {
const save = useAction(fetchVolunteerHostingSet)
const jwtToken = useSelector(selectUserJwtToken)
const userWishes = useSelector(

View File

@ -0,0 +1,69 @@
import { shallowEqual, useSelector } from "react-redux"
import { useCallback } from "react"
import { selectUserJwtToken } from "../../store/auth"
import { AppState } from "../../store"
import { fetchVolunteerMealsSet } from "../../store/volunteerMealsSet"
import useAction from "../../utils/useAction"
import { VolunteerMeals } from "../../services/volunteers"
export type MealOption = { abbr: string; title: string }
export type MealDay = {
name: string
options: MealOption[]
}
export const mealDays: MealDay[] = [
{
name: "Samedi midi",
options: [
{ abbr: "V", title: "Sandwich végétarien" },
{ abbr: "F", title: "Sandwich fromage" },
{ abbr: "P", title: "Sandwich poulet" },
{ abbr: "", title: "Pas de repas" },
],
},
{
name: "Dimanche midi",
options: [
{ abbr: "V", title: "Sandwich végétarien" },
{ abbr: "F", title: "Sandwich fromage" },
{ abbr: "P", title: "Sandwich poulet" },
{ abbr: "", title: "Pas de repas" },
],
},
{
name: "Dimanche soir",
options: [
{
abbr: "V",
title: "Lasagnes végétariennes accompagnées de ratatouille et haricots verts",
},
{ abbr: "P", title: "Aiguillettes de poulet accompagnées de riz thaï" },
{ abbr: "", title: "Pas de repas" },
],
},
]
type SetFunction = (meals: VolunteerMeals["meals"]) => void
export const useUserMeals = (): [VolunteerMeals | undefined, SetFunction] => {
const save = useAction(fetchVolunteerMealsSet)
const jwtToken = useSelector(selectUserJwtToken)
const userWishes = useSelector(
(state: AppState) => state.volunteerMealsSet?.entity,
shallowEqual
)
const saveWishes = useCallback(
(meals) => {
if (!userWishes) return
save(jwtToken, 0, {
id: userWishes.id,
meals,
})
},
[userWishes, save, jwtToken]
)
return [userWishes, saveWishes]
}

View File

@ -71,8 +71,8 @@ async function sendMeetingEmail(
to: email,
from: "contact@parisestludique.fr",
subject: "Première rencontre Paris est Ludique",
text: `Salut ${firstname},\n\nNous allons te contacter prochainement pour trouver ensemble un moyen de se rencontrer.\n\nÀ bientôt :)\nPierre`,
html: `Salut ${firstname},<br /><br />Nous allons te contacter prochainement pour trouver ensemble un moyen de se rencontrer.<br /><br />À bientôt :)<br />Pierre`,
text: `Salut ${firstname},\n\nTon inscription est bien prise en compte !\n\nNous te contacterons pour trouver un moyen de se rencontrer.\n\nÀ bientôt :)\nPierre`,
html: `Salut ${firstname},<br /><br />Ton inscription est bien prise en compte !<br /><br />Nous te contacterons pour trouver un moyen de se rencontrer.<br /><br />À bientôt :)<br />Pierre`,
}
await sgMail.send(msg)
}

View File

@ -12,6 +12,7 @@ import {
translationVolunteer,
VolunteerDayWishes,
VolunteerHosting,
VolunteerMeals,
VolunteerParticipationDetails,
VolunteerTeamAssign,
VolunteerKnowledge,
@ -362,6 +363,30 @@ export const volunteerHostingSet = expressAccessor.set(async (list, body, id) =>
}
})
export const volunteerMealsSet = expressAccessor.set(async (list, body, id) => {
const requestedId = +body[0] || id
if (requestedId !== id && requestedId !== 0) {
throw Error(`On ne peut acceder qu'à ses propres repas`)
}
const wishes = body[1] as VolunteerMeals
const volunteer: Volunteer | undefined = list.find((v) => v.id === requestedId)
if (!volunteer) {
throw Error(`Il n'y a aucun bénévole avec cet identifiant ${requestedId}`)
}
const newVolunteer = cloneDeep(volunteer)
if (wishes.meals !== undefined) {
newVolunteer.meals = wishes.meals
}
return {
toDatabase: newVolunteer,
toCaller: {
id: newVolunteer.id,
meals: newVolunteer.meals,
} as VolunteerMeals,
}
})
export const volunteerParticipationDetailsSet = expressAccessor.set(async (list, body, id) => {
const requestedId = +body[0] || id
if (requestedId !== id && requestedId !== 0) {

View File

@ -27,9 +27,9 @@ import {
volunteerDayWishesSet,
volunteerForgot,
volunteerHostingSet,
volunteerMealsSet,
volunteerDiscordId,
volunteerLogin,
volunteerPartialAdd,
volunteerParticipationDetailsSet,
volunteerSet,
volunteerTeamWishesSet,
@ -94,7 +94,7 @@ app.get("/MiscMeetingDateListGet", miscMeetingDateListGet)
app.get("/WishListGet", wishListGet)
app.post("/WishAdd", wishAdd)
app.post("/PostulantAdd", postulantAdd)
app.post("/VolunteerPartialAdd", volunteerPartialAdd)
// Disabling registration app.post("/VolunteerPartialAdd", volunteerPartialAdd)
app.post("/VolunteerLogin", volunteerLogin)
app.post("/VolunteerForgot", volunteerForgot)
app.get("/VolunteerListGet", secure as RequestHandler, volunteerListGet)
@ -113,6 +113,7 @@ app.post(
)
app.post("/VolunteerDayWishesSet", secure as RequestHandler, volunteerDayWishesSet)
app.post("/VolunteerHostingSet", secure as RequestHandler, volunteerHostingSet)
app.post("/VolunteerMealsSet", secure as RequestHandler, volunteerMealsSet)
app.post("/VolunteerTeamWishesSet", secure as RequestHandler, volunteerTeamWishesSet)
app.post("/VolunteerTeamAssignSet", secure as RequestHandler, volunteerTeamAssignSet)

View File

@ -67,6 +67,8 @@ export class Volunteer implements VolunteerPartial {
distanceToFestival = 0
hostingComment = ""
meals: string[] = []
}
export const translationVolunteer: { [k in keyof Volunteer]: string } = {
@ -104,6 +106,7 @@ export const translationVolunteer: { [k in keyof Volunteer]: string } = {
canHostCount: "nombreHébergés",
distanceToFestival: "distanceAuFestival",
hostingComment: "commentaireHébergement",
meals: "repas",
}
export class VolunteerPartial {
@ -153,6 +156,7 @@ export const volunteerExample: Volunteer = {
canHostCount: 0,
distanceToFestival: 0,
hostingComment: "",
meals: [],
}
export const emailRegexp =
@ -205,6 +209,11 @@ export interface VolunteerHosting {
hostingComment: Volunteer["hostingComment"]
}
export interface VolunteerMeals {
id: Volunteer["id"]
meals: Volunteer["meals"]
}
export interface VolunteerParticipationDetails {
id: Volunteer["id"]
tshirtSize: Volunteer["tshirtSize"]

View File

@ -11,6 +11,7 @@ import {
VolunteerWithoutId,
VolunteerDiscordId,
VolunteerKnowledge,
VolunteerMeals,
} from "./volunteers"
const serviceAccessors = new ServiceAccessors<VolunteerWithoutId, Volunteer>(elementName)
@ -42,6 +43,9 @@ export const volunteerDayWishesSet =
export const volunteerHostingSet =
serviceAccessors.securedCustomPost<[number, Partial<VolunteerHosting>]>("HostingSet")
export const volunteerMealsSet =
serviceAccessors.securedCustomPost<[number, Partial<VolunteerMeals>]>("MealsSet")
export const volunteerParticipationDetailsSet =
serviceAccessors.securedCustomPost<[number, Partial<VolunteerParticipationDetails>]>(
"ParticipationDetailsSet"

View File

@ -17,6 +17,7 @@ import volunteerDayWishesSet from "./volunteerDayWishesSet"
import volunteerDiscordId from "./volunteerDiscordId"
import volunteerForgot from "./volunteerForgot"
import volunteerHostingSet from "./volunteerHostingSet"
import volunteerMealsSet from "./volunteerMealsSet"
import volunteerList from "./volunteerList"
import volunteerLogin from "./volunteerLogin"
import volunteerKnowledgeSet from "./volunteerKnowledgeSet"
@ -46,6 +47,7 @@ export default (history: History) => ({
volunteerDiscordId,
volunteerForgot,
volunteerHostingSet,
volunteerMealsSet,
volunteerList,
volunteerLogin,
volunteerKnowledgeSet,

View File

@ -29,6 +29,7 @@ export const selectActiveModalId = createSelector(selectUiData, (ui) => ui.modal
export const MODAL_IDS = {
DAYWISHES: "DAYWISHES",
HOSTING: "HOSTING",
MEALS: "MEALS",
PARTICIPATIONDETAILS: "PARTICIPATIONDETAILS",
TEAMWISHES: "TEAMWISHES",
}

View File

@ -0,0 +1,60 @@
import { PayloadAction, createSlice } from "@reduxjs/toolkit"
import { StateRequest, toastError, elementFetch } from "./utils"
import { VolunteerMeals } from "../services/volunteers"
import { AppThunk, AppState } from "."
import { volunteerMealsSet } from "../services/volunteersAccessors"
type StateVolunteerMealsSet = { entity?: VolunteerMeals } & StateRequest
export const initialState: StateVolunteerMealsSet = {
readyStatus: "idle",
}
const volunteerMealsSetSlice = createSlice({
name: "volunteerMealsSet",
initialState,
reducers: {
getRequesting: (_) => ({
readyStatus: "request",
}),
getSuccess: (_, { payload }: PayloadAction<VolunteerMeals>) => ({
readyStatus: "success",
entity: payload,
}),
getFailure: (_, { payload }: PayloadAction<string>) => ({
readyStatus: "failure",
error: payload,
}),
},
})
export default volunteerMealsSetSlice.reducer
export const { getRequesting, getSuccess, getFailure } = volunteerMealsSetSlice.actions
export const fetchVolunteerMealsSet = elementFetch(
volunteerMealsSet,
getRequesting,
getSuccess,
getFailure,
(error: Error) =>
toastError(`Erreur lors du chargement des choix de jours de présence: ${error.message}`)
)
const shouldFetchVolunteerMealsSet = (state: AppState, id: number) =>
state.volunteerMealsSet?.readyStatus !== "success" ||
(state.volunteerMealsSet?.entity && state.volunteerMealsSet?.entity?.id !== id)
export const fetchVolunteerMealsSetIfNeed =
(id = 0, wishes: Partial<VolunteerMeals> = {}): AppThunk =>
(dispatch, getState) => {
let jwt = ""
if (!id) {
;({ jwt, id } = getState().auth)
}
if (shouldFetchVolunteerMealsSet(getState(), id))
return dispatch(fetchVolunteerMealsSet(jwt, id, wishes))
return null
}