diff --git a/src/server/gsheets/volunteers.ts b/src/server/gsheets/volunteers.ts index a3e5b46..26cdd72 100644 --- a/src/server/gsheets/volunteers.ts +++ b/src/server/gsheets/volunteers.ts @@ -11,6 +11,7 @@ import { VolunteerTeamWishes, translationVolunteer, VolunteerDayWishes, + VolunteerParticipationDetails, } from "../../services/volunteers" import { canonicalEmail } from "../../utils/standardization" import { getJwt } from "../secure" @@ -87,6 +88,32 @@ export const volunteerForgot = expressAccessor.set(async (list, bodyArray) => { } }) +function generatePassword(): string { + const s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + return Array(16) + .join() + .split(",") + .map(() => s.charAt(Math.floor(Math.random() * s.length))) + .join("") +} + +async function sendForgetEmail(email: string, password: string): Promise { + const apiKey = process.env.SENDGRID_API_KEY || "" + if (__DEV__ || apiKey === "") { + console.error(`Fake sending forget email to ${email} with password ${password}`) + } else { + sgMail.setApiKey(apiKey) + const msg = { + to: email, + from: "contact@parisestludique.fr", + subject: "Nouveau mot de passe pour le site de Paris est Ludique", + text: `Voici le nouveau mot de passe : ${password}\nL'ancien fonctionne encore, si tu t'en rappelles.`, + html: `Voici le nouveau mot de passe : ${password}
L'ancien fonctionne encore, si tu t'en rappelles.`, + } + await sgMail.send(msg) + } +} + export const volunteerNotifsSet = expressAccessor.set(async (list, body, id) => { const requestedId = +body[0] || id if (requestedId !== id && requestedId !== 0) { @@ -172,34 +199,42 @@ export const volunteerDayWishesSet = expressAccessor.set(async (list, body, id) } as VolunteerDayWishes, } }) -function getByEmail(list: Volunteer[], rawEmail: string): Volunteer | undefined { + +export const volunteerParticipationDetailsSet = 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 infos d'age, taille et alimentation`) + } + const wishes = body[1] as VolunteerParticipationDetails + const volunteer = 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.age !== undefined) { + newVolunteer.age = wishes.age + } + if (wishes.teeshirtSize !== undefined) { + newVolunteer.teeshirtSize = wishes.teeshirtSize + } + if (wishes.food !== undefined) { + newVolunteer.food = wishes.food + } + + return { + toDatabase: newVolunteer, + toCaller: { + id: newVolunteer.id, + age: newVolunteer.age, + teeshirtSize: newVolunteer.teeshirtSize, + food: newVolunteer.food, + } as VolunteerParticipationDetails, + } +}) + +function getByEmail(list: T[], rawEmail: string): T | undefined { const email = canonicalEmail(rawEmail || "") const volunteer = list.find((v) => canonicalEmail(v.email) === email) return volunteer } - -function generatePassword(): string { - const s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - return Array(16) - .join() - .split(",") - .map(() => s.charAt(Math.floor(Math.random() * s.length))) - .join("") -} - -async function sendForgetEmail(email: string, password: string): Promise { - const apiKey = process.env.SENDGRID_API_KEY || "" - if (__DEV__ || apiKey === "") { - console.error(`Fake sending forget email to ${email} with password ${password}`) - } else { - sgMail.setApiKey(apiKey) - const msg = { - to: email, - from: "contact@parisestludique.fr", - subject: "Nouveau mot de passe pour le site de Paris est Ludique", - text: `Voici le nouveau mot de passe : ${password}\nL'ancien fonctionne encore, si tu t'en rappelles.`, - html: `Voici le nouveau mot de passe : ${password}
L'ancien fonctionne encore, si tu t'en rappelles.`, - } - await sgMail.send(msg) - } -} diff --git a/src/server/index.ts b/src/server/index.ts index 6a859cb..361c4d8 100755 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -25,6 +25,7 @@ import { volunteerLogin, volunteerForgot, volunteerNotifsSet, + volunteerParticipationDetailsSet, volunteerTeamWishesSet, volunteerDayWishesSet, } from "./gsheets/volunteers" @@ -77,6 +78,11 @@ app.post("/VolunteerSet", secure as RequestHandler, volunteerSet) app.get("/TeamListGet", teamListGet) // UNSAFE app.post("/VolunteerGet", secure as RequestHandler, volunteerGet) app.post("/VolunteerNotifsSet", secure as RequestHandler, volunteerNotifsSet) +app.post( + "/VolunteerParticipationDetailsSet", + secure as RequestHandler, + volunteerParticipationDetailsSet +) app.post("/VolunteerDayWishesSet", secure as RequestHandler, volunteerDayWishesSet) app.post("/VolunteerTeamWishesSet", secure as RequestHandler, volunteerTeamWishesSet) diff --git a/src/services/volunteers.ts b/src/services/volunteers.ts index 952cdec..fea8e6e 100644 --- a/src/services/volunteers.ts +++ b/src/services/volunteers.ts @@ -13,8 +13,6 @@ export class Volunteer { photo = "" - food = "" - adult = 1 privileges = 0 @@ -25,6 +23,12 @@ export class Volunteer { dayWishesComment = "" + age = 0 + + teeshirtSize = "" + + food = "" + teamWishes: string[] = [] teamWishesComment = "" @@ -49,12 +53,14 @@ export const translationVolunteer: { [k in keyof Volunteer]: string } = { email: "mail", mobile: "telephone", photo: "photo", - food: "alimentation", adult: "majeur", privileges: "privilege", active: "actif", dayWishes: "enviesJours", dayWishesComment: "commentaireEnviesJours", + age: "age", + teeshirtSize: "teeshirt", + food: "alimentation", teamWishes: "enviesEquipe", teamWishesComment: "commentaireEnviesEquipe", hiddenNotifs: "notifsCachees", @@ -74,12 +80,14 @@ export const volunteerExample: Volunteer = { email: "pakouille.lakouille@yahoo.fr", mobile: "0675650392", photo: "images/volunteers/$taille/amélie_aupeix.jpg", - food: "Végétarien", adult: 1, privileges: 0, active: "inconnu", dayWishes: [], dayWishesComment: "", + age: 33, + teeshirtSize: "FM", + food: "Végétarien", teamWishes: [], teamWishesComment: "", hiddenNotifs: [], @@ -142,3 +150,14 @@ export interface VolunteerDayWishes { } export const volunteerDayWishesSet = serviceAccessors.securedCustomPost<[number, Partial]>("DayWishesSet") + +export interface VolunteerParticipationDetails { + id: Volunteer["id"] + age: Volunteer["age"] + teeshirtSize: Volunteer["teeshirtSize"] + food: Volunteer["food"] +} +export const volunteerParticipationDetailsSet = + serviceAccessors.securedCustomPost<[number, Partial]>( + "ParticipationDetailsSet" + ) diff --git a/src/store/rootReducer.ts b/src/store/rootReducer.ts index 9362553..329f8ac 100644 --- a/src/store/rootReducer.ts +++ b/src/store/rootReducer.ts @@ -13,6 +13,7 @@ import volunteerSet from "./volunteerSet" import volunteerLogin from "./volunteerLogin" import volunteerForgot from "./volunteerForgot" import volunteerNotifsSet from "./volunteerNotifsSet" +import volunteerParticipationDetailsSet from "./volunteerParticipationDetailsSet" import volunteerDayWishesSet from "./volunteerDayWishesSet" import volunteerTeamWishesSet from "./volunteerTeamWishesSet" import wishAdd from "./wishAdd" @@ -33,6 +34,7 @@ export default (history: History) => ({ volunteerLogin, volunteerForgot, volunteerNotifsSet, + volunteerParticipationDetailsSet, volunteerDayWishesSet, volunteerTeamWishesSet, wishAdd, diff --git a/src/store/volunteerParticipationDetailsSet.ts b/src/store/volunteerParticipationDetailsSet.ts new file mode 100644 index 0000000..1d9c769 --- /dev/null +++ b/src/store/volunteerParticipationDetailsSet.ts @@ -0,0 +1,65 @@ +import { PayloadAction, createSlice } from "@reduxjs/toolkit" + +import { StateRequest, toastError, elementFetch } from "./utils" +import { + VolunteerParticipationDetails, + volunteerParticipationDetailsSet, +} from "../services/volunteers" +import { AppThunk, AppState } from "." + +type StateVolunteerParticipationDetailsSet = { + entity?: VolunteerParticipationDetails +} & StateRequest + +export const initialState: StateVolunteerParticipationDetailsSet = { + readyStatus: "idle", +} + +const volunteerParticipationDetailsSetSlice = createSlice({ + name: "volunteerParticipationDetailsSet", + initialState, + reducers: { + getRequesting: (_) => ({ + readyStatus: "request", + }), + getSuccess: (_, { payload }: PayloadAction) => ({ + readyStatus: "success", + entity: payload, + }), + getFailure: (_, { payload }: PayloadAction) => ({ + readyStatus: "failure", + error: payload, + }), + }, +}) + +export default volunteerParticipationDetailsSetSlice.reducer +export const { getRequesting, getSuccess, getFailure } = + volunteerParticipationDetailsSetSlice.actions + +export const fetchVolunteerParticipationDetailsSet = elementFetch( + volunteerParticipationDetailsSet, + getRequesting, + getSuccess, + getFailure, + (error: Error) => toastError(`Erreur lors du chargement des notifications: ${error.message}`) +) + +const shouldFetchVolunteerParticipationDetailsSet = (state: AppState, id: number) => + state.volunteerParticipationDetailsSet?.readyStatus !== "success" || + (state.volunteerParticipationDetailsSet?.entity && + state.volunteerParticipationDetailsSet?.entity?.id !== id) + +export const fetchVolunteerParticipationDetailsSetIfNeed = + (id = 0, wishes: Partial = {}): AppThunk => + (dispatch, getState) => { + let jwt = "" + + if (!id) { + ;({ id, jwt } = getState().auth) + } + if (shouldFetchVolunteerParticipationDetailsSet(getState(), id)) + return dispatch(fetchVolunteerParticipationDetailsSet(jwt, id, wishes)) + + return null + }