Add volunteerDayWishesSet in store

This commit is contained in:
pikiou 2022-01-21 07:34:50 +01:00
parent fc65a2d42b
commit 93e82a36ee
11 changed files with 256 additions and 38 deletions

View File

@ -0,0 +1,93 @@
import { FC, memo, useCallback, useEffect, useState } from "react"
import { RouteComponentProps } from "react-router-dom"
import { useSelector, shallowEqual, useDispatch } from "react-redux"
import { AppState, AppThunk } from "../../store"
import {
fetchVolunteerDayWishesSet,
fetchVolunteerDayWishesSetIfNeed,
} from "../../store/volunteerDayWishesSet"
import { VolunteerDayWishes } from "../../services/volunteers"
import { selectUserJwtToken } from "../../store/auth"
export type Props = RouteComponentProps
let prevWishes: VolunteerDayWishes | undefined
const HomePage: FC<Props> = (): JSX.Element => {
const dispatch = useDispatch()
const jwtToken = useSelector(selectUserJwtToken)
const wishesForm = useSelector((state: AppState) => {
const wishes = state.volunteerDayWishesSet?.entity
if (wishes) {
prevWishes = wishes
return wishes
}
return prevWishes
}, shallowEqual)
const [dayWishes, setDayWishes] = useState(wishesForm?.dayWishes.join(",") || "")
const [dayWishesComment, setDayWishesComment] = useState(wishesForm?.dayWishesComment || "")
useEffect(() => {
setDayWishes(wishesForm?.dayWishes.join(",") || "")
setDayWishesComment(wishesForm?.dayWishesComment || "")
}, [wishesForm])
const onDayWishesChanged = (e: React.ChangeEvent<HTMLInputElement>) =>
setDayWishes(e.target.value)
const onDayWishesCommentChanged = (e: React.ChangeEvent<HTMLInputElement>) =>
setDayWishesComment(e.target.value)
const onSubmit = useCallback(
(event: React.SyntheticEvent): void => {
event.preventDefault()
if (!wishesForm) {
console.error("NO FORM WISHES RECEIVED")
return // Form should not even appear if this happens
}
dispatch(
fetchVolunteerDayWishesSet(jwtToken, 0, {
id: wishesForm.id,
dayWishes: (dayWishes || "").split(","),
dayWishesComment,
})
)
},
[dispatch, jwtToken, wishesForm, dayWishes, dayWishesComment]
)
if (jwtToken === undefined) return <p>Loading...</p>
if (jwtToken) {
return (
<form>
<input
type="text"
id="dayWishes"
required
value={dayWishes}
onChange={onDayWishesChanged}
/>
<br />
<input
type="text"
id="dayWishesComment"
required
value={dayWishesComment}
onChange={onDayWishesCommentChanged}
/>
<button type="button" onClick={onSubmit}>
Envoyer
</button>
</form>
)
}
return <div>Besoin d&apos;être identifié</div>
}
// Fetch server-side data here
export const loadData = (): AppThunk[] => [fetchVolunteerDayWishesSetIfNeed()]
export default memo(HomePage)

16
src/pages/DayWishes/index.tsx Executable file
View File

@ -0,0 +1,16 @@
import loadable from "@loadable/component"
import { Loading, ErrorBoundary } from "../../components"
import { Props, loadData } from "./DayWishes"
const HomePage = loadable(() => import("./DayWishes"), {
fallback: <Loading />,
})
export default (props: Props): JSX.Element => (
<ErrorBoundary>
<HomePage {...props} />
</ErrorBoundary>
)
export { loadData }

View File

@ -0,0 +1 @@
@import "../../theme/mixins";

View File

@ -28,17 +28,17 @@ const HomePage: FC<Props> = (): JSX.Element => {
}, shallowEqual) }, shallowEqual)
const [teamWishes, setTeamWishes] = useState(wishesForm?.teamWishes.join(",") || "") const [teamWishes, setTeamWishes] = useState(wishesForm?.teamWishes.join(",") || "")
const [teamWishComment, setTeamWishComment] = useState(wishesForm?.teamWishComment || "") const [teamWishesComment, setTeamWishesComment] = useState(wishesForm?.teamWishesComment || "")
useEffect(() => { useEffect(() => {
setTeamWishes(wishesForm?.teamWishes.join(",") || "") setTeamWishes(wishesForm?.teamWishes.join(",") || "")
setTeamWishComment(wishesForm?.teamWishComment || "") setTeamWishesComment(wishesForm?.teamWishesComment || "")
}, [wishesForm]) }, [wishesForm])
const onTeamWishesChanged = (e: React.ChangeEvent<HTMLInputElement>) => const onTeamWishesChanged = (e: React.ChangeEvent<HTMLInputElement>) =>
setTeamWishes(e.target.value) setTeamWishes(e.target.value)
const onTeamWishCommentChanged = (e: React.ChangeEvent<HTMLInputElement>) => const onTeamWishesCommentChanged = (e: React.ChangeEvent<HTMLInputElement>) =>
setTeamWishComment(e.target.value) setTeamWishesComment(e.target.value)
const onSubmit = useCallback( const onSubmit = useCallback(
(event: React.SyntheticEvent): void => { (event: React.SyntheticEvent): void => {
@ -51,11 +51,11 @@ const HomePage: FC<Props> = (): JSX.Element => {
fetchVolunteerTeamWishesSet(jwtToken, 0, { fetchVolunteerTeamWishesSet(jwtToken, 0, {
id: wishesForm.id, id: wishesForm.id,
teamWishes: (teamWishes || "").split(","), teamWishes: (teamWishes || "").split(","),
teamWishComment, teamWishesComment,
}) })
) )
}, },
[dispatch, jwtToken, wishesForm, teamWishes, teamWishComment] [dispatch, jwtToken, wishesForm, teamWishes, teamWishesComment]
) )
if (jwtToken === undefined) return <p>Loading...</p> if (jwtToken === undefined) return <p>Loading...</p>
@ -73,10 +73,10 @@ const HomePage: FC<Props> = (): JSX.Element => {
<br /> <br />
<input <input
type="text" type="text"
id="teamWishComment" id="teamWishesComment"
required required
value={teamWishComment} value={teamWishesComment}
onChange={onTeamWishCommentChanged} onChange={onTeamWishesCommentChanged}
/> />
<button type="button" onClick={onSubmit}> <button type="button" onClick={onSubmit}>
Envoyer Envoyer

View File

@ -4,6 +4,7 @@ import App from "../app"
import AsyncHome, { loadData as loadHomeData } from "../pages/Home" import AsyncHome, { loadData as loadHomeData } from "../pages/Home"
import AsyncPreRegisterPage, { loadData as loadPreRegisterPage } from "../pages/PreRegister" import AsyncPreRegisterPage, { loadData as loadPreRegisterPage } from "../pages/PreRegister"
import AsyncTeams, { loadData as loadTeamsData } from "../pages/Teams" import AsyncTeams, { loadData as loadTeamsData } from "../pages/Teams"
import AsyncDayWishes, { loadData as loadDayWishesData } from "../pages/DayWishes"
import AsyncTeamWishes, { loadData as loadTeamWishesData } from "../pages/TeamWishes" import AsyncTeamWishes, { loadData as loadTeamWishesData } from "../pages/TeamWishes"
import AsyncWish, { loadData as loadWishData } from "../pages/Wish" import AsyncWish, { loadData as loadWishData } from "../pages/Wish"
import AsyncVolunteerPage, { loadData as loadVolunteerPageData } from "../pages/VolunteerPage" import AsyncVolunteerPage, { loadData as loadVolunteerPageData } from "../pages/VolunteerPage"
@ -44,6 +45,11 @@ export default [
component: AsyncTeams, component: AsyncTeams,
loadData: loadTeamsData, loadData: loadTeamsData,
}, },
{
path: "/dayWishes",
component: AsyncDayWishes,
loadData: loadDayWishesData,
},
{ {
path: "/teamWishes", path: "/teamWishes",
component: AsyncTeamWishes, component: AsyncTeamWishes,

View File

@ -10,6 +10,7 @@ import {
VolunteerNotifs, VolunteerNotifs,
VolunteerTeamWishes, VolunteerTeamWishes,
translationVolunteer, translationVolunteer,
VolunteerDayWishes,
} from "../../services/volunteers" } from "../../services/volunteers"
import { canonicalEmail } from "../../utils/standardization" import { canonicalEmail } from "../../utils/standardization"
import { getJwt } from "../secure" import { getJwt } from "../secure"
@ -92,14 +93,14 @@ export const volunteerForgot = expressAccessor.set(
export const volunteerNotifsSet = expressAccessor.set( export const volunteerNotifsSet = expressAccessor.set(
async (list: Volunteer[], body: RequestBody, id: number) => { async (list: Volunteer[], body: RequestBody, id: number) => {
const requestedId = +body[0] const requestedId = +body[0] || id
if (requestedId !== id && requestedId !== 0) { if (requestedId !== id && requestedId !== 0) {
throw Error(`On ne peut acceder qu'à ses propres notifs`) throw Error(`On ne peut acceder qu'à ses propres notifs`)
} }
const notifChanges = body[1] const notifChanges = body[1]
const volunteer = list.find((v) => v.id === id) const volunteer = list.find((v) => v.id === requestedId)
if (!volunteer) { if (!volunteer) {
throw Error(`Il n'y a aucun bénévole avec cet identifiant ${id}`) throw Error(`Il n'y a aucun bénévole avec cet identifiant ${requestedId}`)
} }
const newVolunteer = _.cloneDeep(volunteer) const newVolunteer = _.cloneDeep(volunteer)
@ -122,23 +123,22 @@ export const volunteerNotifsSet = expressAccessor.set(
export const volunteerTeamWishesSet = expressAccessor.set( export const volunteerTeamWishesSet = expressAccessor.set(
async (list: Volunteer[], body: RequestBody, id: number) => { async (list: Volunteer[], body: RequestBody, id: number) => {
console.log("volunteerTeamWishesSet", body) const requestedId = +body[0] || id
const requestedId = +body[0]
if (requestedId !== id && requestedId !== 0) { if (requestedId !== id && requestedId !== 0) {
throw Error(`On ne peut acceder qu'à ses propres notifs`) throw Error(`On ne peut acceder qu'à ses propres envies d'équipes`)
} }
const wishes = body[1] as VolunteerTeamWishes const wishes = body[1] as VolunteerTeamWishes
const volunteer = list.find((v) => v.id === id) const volunteer = list.find((v) => v.id === requestedId)
if (!volunteer) { if (!volunteer) {
throw Error(`Il n'y a aucun bénévole avec cet identifiant ${id}`) throw Error(`Il n'y a aucun bénévole avec cet identifiant ${requestedId}`)
} }
const newVolunteer = _.cloneDeep(volunteer) const newVolunteer = _.cloneDeep(volunteer)
if (wishes.teamWishes !== undefined) { if (wishes.teamWishes !== undefined) {
newVolunteer.teamWishes = wishes.teamWishes newVolunteer.teamWishes = wishes.teamWishes
} }
if (wishes.teamWishComment !== undefined) { if (wishes.teamWishesComment !== undefined) {
newVolunteer.teamWishComment = wishes.teamWishComment newVolunteer.teamWishesComment = wishes.teamWishesComment
} }
return { return {
@ -146,12 +146,42 @@ export const volunteerTeamWishesSet = expressAccessor.set(
toCaller: { toCaller: {
id: newVolunteer.id, id: newVolunteer.id,
teamWishes: newVolunteer.teamWishes, teamWishes: newVolunteer.teamWishes,
teamWishComment: newVolunteer.teamWishComment, teamWishesComment: newVolunteer.teamWishesComment,
} as VolunteerTeamWishes, } as VolunteerTeamWishes,
} }
} }
) )
export const volunteerDayWishesSet = expressAccessor.set(
async (list: Volunteer[], body: RequestBody, id: number) => {
const requestedId = +body[0] || id
if (requestedId !== id && requestedId !== 0) {
throw Error(`On ne peut acceder qu'à ses propres envies de jours`)
}
const wishes = body[1] as VolunteerDayWishes
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.dayWishes !== undefined) {
newVolunteer.dayWishes = wishes.dayWishes
}
if (wishes.dayWishesComment !== undefined) {
newVolunteer.dayWishesComment = wishes.dayWishesComment
}
return {
toDatabase: newVolunteer,
toCaller: {
id: newVolunteer.id,
dayWishes: newVolunteer.dayWishes,
dayWishesComment: newVolunteer.dayWishesComment,
} as VolunteerDayWishes,
}
}
)
function getByEmail(list: Volunteer[], rawEmail: string): Volunteer | undefined { function getByEmail(list: Volunteer[], rawEmail: string): Volunteer | undefined {
const email = canonicalEmail(rawEmail || "") const email = canonicalEmail(rawEmail || "")
const volunteer = list.find((v) => canonicalEmail(v.email) === email) const volunteer = list.find((v) => canonicalEmail(v.email) === email)

View File

@ -26,6 +26,7 @@ import {
volunteerForgot, volunteerForgot,
volunteerNotifsSet, volunteerNotifsSet,
volunteerTeamWishesSet, volunteerTeamWishesSet,
volunteerDayWishesSet,
} from "./gsheets/volunteers" } from "./gsheets/volunteers"
import { wishListGet, wishAdd } from "./gsheets/wishes" import { wishListGet, wishAdd } from "./gsheets/wishes"
import config from "../config" import config from "../config"
@ -76,6 +77,7 @@ app.post("/VolunteerSet", secure as RequestHandler, volunteerSet)
app.get("/TeamListGet", teamListGet) app.get("/TeamListGet", teamListGet)
// UNSAFE app.post("/VolunteerGet", secure as RequestHandler, volunteerGet) // UNSAFE app.post("/VolunteerGet", secure as RequestHandler, volunteerGet)
app.post("/VolunteerNotifsSet", secure as RequestHandler, volunteerNotifsSet) app.post("/VolunteerNotifsSet", secure as RequestHandler, volunteerNotifsSet)
app.post("/VolunteerDayWishesSet", secure as RequestHandler, volunteerDayWishesSet)
app.post("/VolunteerTeamWishesSet", secure as RequestHandler, volunteerTeamWishesSet) app.post("/VolunteerTeamWishesSet", secure as RequestHandler, volunteerTeamWishesSet)
// Push notification subscription // Push notification subscription

View File

@ -14,8 +14,6 @@ export default function notificationsSubscribe(
): void { ): void {
const subscription = request.body const subscription = request.body
console.log(subscription)
const payload = JSON.stringify({ const payload = JSON.stringify({
title: "Hello!", title: "Hello!",
body: "It works.", body: "It works.",

View File

@ -21,11 +21,13 @@ export class Volunteer {
active = "" active = ""
participingDays = [] dayWishes: string[] = []
dayWishesComment = ""
teamWishes: string[] = [] teamWishes: string[] = []
teamWishComment = "" teamWishesComment = ""
hiddenNotifs: number[] = [] hiddenNotifs: number[] = []
@ -51,9 +53,10 @@ export const translationVolunteer: { [k in keyof Volunteer]: string } = {
adult: "majeur", adult: "majeur",
privileges: "privilege", privileges: "privilege",
active: "actif", active: "actif",
participingDays: "joursPrésent", dayWishes: "enviesJours",
dayWishesComment: "commentaireEnviesJours",
teamWishes: "enviesEquipe", teamWishes: "enviesEquipe",
teamWishComment: "commentaireEnviesEquipe", teamWishesComment: "commentaireEnviesEquipe",
hiddenNotifs: "notifsCachees", hiddenNotifs: "notifsCachees",
created: "creation", created: "creation",
password1: "passe1", password1: "passe1",
@ -75,9 +78,10 @@ export const volunteerExample: Volunteer = {
adult: 1, adult: 1,
privileges: 0, privileges: 0,
active: "inconnu", active: "inconnu",
participingDays: [], dayWishes: [],
dayWishesComment: "",
teamWishes: [], teamWishes: [],
teamWishComment: "", teamWishesComment: "",
hiddenNotifs: [], hiddenNotifs: [],
created: new Date(0), created: new Date(0),
password1: "$2y$10$fSxY9AIuxSiEjwF.J3eXGubIxUPkdq9d5fqpbl8ASimSjNj4SR.9O", password1: "$2y$10$fSxY9AIuxSiEjwF.J3eXGubIxUPkdq9d5fqpbl8ASimSjNj4SR.9O",
@ -112,21 +116,29 @@ export interface VolunteerForgot {
export const volunteerForgot = serviceAccessors.customPost<[{ email: string }]>("Forgot") export const volunteerForgot = serviceAccessors.customPost<[{ email: string }]>("Forgot")
export interface VolunteerNotifs { export interface VolunteerNotifs {
id: number id: Volunteer["id"]
firstname: string firstname: Volunteer["firstname"]
adult: number adult: Volunteer["adult"]
active: string active: Volunteer["active"]
hiddenNotifs: number[] hiddenNotifs: Volunteer["hiddenNotifs"]
pushNotifSubscription: string pushNotifSubscription: Volunteer["pushNotifSubscription"]
acceptsNotifs: string acceptsNotifs: Volunteer["acceptsNotifs"]
} }
export const volunteerNotifsSet = export const volunteerNotifsSet =
serviceAccessors.securedCustomPost<[number, Partial<VolunteerNotifs>]>("NotifsSet") serviceAccessors.securedCustomPost<[number, Partial<VolunteerNotifs>]>("NotifsSet")
export interface VolunteerTeamWishes { export interface VolunteerTeamWishes {
id: number id: Volunteer["id"]
teamWishes: string[] teamWishes: Volunteer["teamWishes"]
teamWishComment: string teamWishesComment: Volunteer["teamWishesComment"]
} }
export const volunteerTeamWishesSet = export const volunteerTeamWishesSet =
serviceAccessors.securedCustomPost<[number, Partial<VolunteerTeamWishes>]>("TeamWishesSet") serviceAccessors.securedCustomPost<[number, Partial<VolunteerTeamWishes>]>("TeamWishesSet")
export interface VolunteerDayWishes {
id: Volunteer["id"]
dayWishes: Volunteer["dayWishes"]
dayWishesComment: Volunteer["dayWishesComment"]
}
export const volunteerDayWishesSet =
serviceAccessors.securedCustomPost<[number, Partial<VolunteerDayWishes>]>("DayWishesSet")

View File

@ -13,6 +13,7 @@ import volunteerSet from "./volunteerSet"
import volunteerLogin from "./volunteerLogin" import volunteerLogin from "./volunteerLogin"
import volunteerForgot from "./volunteerForgot" import volunteerForgot from "./volunteerForgot"
import volunteerNotifsSet from "./volunteerNotifsSet" import volunteerNotifsSet from "./volunteerNotifsSet"
import volunteerDayWishesSet from "./volunteerDayWishesSet"
import volunteerTeamWishesSet from "./volunteerTeamWishesSet" import volunteerTeamWishesSet from "./volunteerTeamWishesSet"
import wishAdd from "./wishAdd" import wishAdd from "./wishAdd"
import wishList from "./wishList" import wishList from "./wishList"
@ -32,6 +33,7 @@ export default (history: History) => ({
volunteerLogin, volunteerLogin,
volunteerForgot, volunteerForgot,
volunteerNotifsSet, volunteerNotifsSet,
volunteerDayWishesSet,
volunteerTeamWishesSet, volunteerTeamWishesSet,
wishAdd, wishAdd,
wishList, wishList,

View File

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