From fc65a2d42b992ca5ba6dbbe9e8b8bf61282a7a05 Mon Sep 17 00:00:00 2001 From: pikiou Date: Fri, 21 Jan 2022 01:34:32 +0100 Subject: [PATCH] Add volunteerTeamWishesSet in store --- src/pages/TeamWishes/TeamWishes.tsx | 93 +++++++++++++++++++++++++ src/pages/TeamWishes/index.tsx | 16 +++++ src/pages/TeamWishes/styles.module.scss | 1 + src/routes/index.ts | 6 ++ src/server/gsheets/volunteers.ts | 33 +++++++++ src/server/index.ts | 4 +- src/services/volunteers.ts | 20 ++++++ src/store/rootReducer.ts | 2 + src/store/volunteerTeamWishesSet.ts | 58 +++++++++++++++ 9 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 src/pages/TeamWishes/TeamWishes.tsx create mode 100755 src/pages/TeamWishes/index.tsx create mode 100755 src/pages/TeamWishes/styles.module.scss create mode 100644 src/store/volunteerTeamWishesSet.ts diff --git a/src/pages/TeamWishes/TeamWishes.tsx b/src/pages/TeamWishes/TeamWishes.tsx new file mode 100644 index 0000000..a3b1854 --- /dev/null +++ b/src/pages/TeamWishes/TeamWishes.tsx @@ -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 { + fetchVolunteerTeamWishesSet, + fetchVolunteerTeamWishesSetIfNeed, +} from "../../store/volunteerTeamWishesSet" +import { VolunteerTeamWishes } from "../../services/volunteers" +import { selectUserJwtToken } from "../../store/auth" + +export type Props = RouteComponentProps + +let prevWishes: VolunteerTeamWishes | undefined + +const HomePage: FC = (): JSX.Element => { + const dispatch = useDispatch() + const jwtToken = useSelector(selectUserJwtToken) + + const wishesForm = useSelector((state: AppState) => { + const wishes = state.volunteerTeamWishesSet?.entity + if (wishes) { + prevWishes = wishes + return wishes + } + return prevWishes + }, shallowEqual) + + const [teamWishes, setTeamWishes] = useState(wishesForm?.teamWishes.join(",") || "") + const [teamWishComment, setTeamWishComment] = useState(wishesForm?.teamWishComment || "") + + useEffect(() => { + setTeamWishes(wishesForm?.teamWishes.join(",") || "") + setTeamWishComment(wishesForm?.teamWishComment || "") + }, [wishesForm]) + + const onTeamWishesChanged = (e: React.ChangeEvent) => + setTeamWishes(e.target.value) + const onTeamWishCommentChanged = (e: React.ChangeEvent) => + setTeamWishComment(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( + fetchVolunteerTeamWishesSet(jwtToken, 0, { + id: wishesForm.id, + teamWishes: (teamWishes || "").split(","), + teamWishComment, + }) + ) + }, + [dispatch, jwtToken, wishesForm, teamWishes, teamWishComment] + ) + + if (jwtToken === undefined) return

Loading...

+ + if (jwtToken) { + return ( +
+ +
+ + +
+ ) + } + return
Besoin d'être identifié
+} + +// Fetch server-side data here +export const loadData = (): AppThunk[] => [fetchVolunteerTeamWishesSetIfNeed()] + +export default memo(HomePage) diff --git a/src/pages/TeamWishes/index.tsx b/src/pages/TeamWishes/index.tsx new file mode 100755 index 0000000..1edcaf7 --- /dev/null +++ b/src/pages/TeamWishes/index.tsx @@ -0,0 +1,16 @@ +import loadable from "@loadable/component" + +import { Loading, ErrorBoundary } from "../../components" +import { Props, loadData } from "./TeamWishes" + +const HomePage = loadable(() => import("./TeamWishes"), { + fallback: , +}) + +export default (props: Props): JSX.Element => ( + + + +) + +export { loadData } diff --git a/src/pages/TeamWishes/styles.module.scss b/src/pages/TeamWishes/styles.module.scss new file mode 100755 index 0000000..b5c6f69 --- /dev/null +++ b/src/pages/TeamWishes/styles.module.scss @@ -0,0 +1 @@ +@import "../../theme/mixins"; diff --git a/src/routes/index.ts b/src/routes/index.ts index 4086fbb..290fb0c 100755 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -4,6 +4,7 @@ import App from "../app" import AsyncHome, { loadData as loadHomeData } from "../pages/Home" import AsyncPreRegisterPage, { loadData as loadPreRegisterPage } from "../pages/PreRegister" import AsyncTeams, { loadData as loadTeamsData } from "../pages/Teams" +import AsyncTeamWishes, { loadData as loadTeamWishesData } from "../pages/TeamWishes" import AsyncWish, { loadData as loadWishData } from "../pages/Wish" import AsyncVolunteerPage, { loadData as loadVolunteerPageData } from "../pages/VolunteerPage" import Login from "../pages/Login" @@ -43,6 +44,11 @@ export default [ component: AsyncTeams, loadData: loadTeamsData, }, + { + path: "/teamWishes", + component: AsyncTeamWishes, + loadData: loadTeamWishesData, + }, { path: "/wish", component: AsyncWish, diff --git a/src/server/gsheets/volunteers.ts b/src/server/gsheets/volunteers.ts index 7a183f5..afda08a 100644 --- a/src/server/gsheets/volunteers.ts +++ b/src/server/gsheets/volunteers.ts @@ -8,6 +8,7 @@ import { VolunteerWithoutId, VolunteerLogin, VolunteerNotifs, + VolunteerTeamWishes, translationVolunteer, } from "../../services/volunteers" import { canonicalEmail } from "../../utils/standardization" @@ -119,6 +120,38 @@ export const volunteerNotifsSet = expressAccessor.set( } ) +export const volunteerTeamWishesSet = expressAccessor.set( + async (list: Volunteer[], body: RequestBody, id: number) => { + console.log("volunteerTeamWishesSet", body) + const requestedId = +body[0] + if (requestedId !== id && requestedId !== 0) { + throw Error(`On ne peut acceder qu'à ses propres notifs`) + } + const wishes = body[1] as VolunteerTeamWishes + const volunteer = list.find((v) => v.id === id) + if (!volunteer) { + throw Error(`Il n'y a aucun bénévole avec cet identifiant ${id}`) + } + const newVolunteer = _.cloneDeep(volunteer) + + if (wishes.teamWishes !== undefined) { + newVolunteer.teamWishes = wishes.teamWishes + } + if (wishes.teamWishComment !== undefined) { + newVolunteer.teamWishComment = wishes.teamWishComment + } + + return { + toDatabase: newVolunteer, + toCaller: { + id: newVolunteer.id, + teamWishes: newVolunteer.teamWishes, + teamWishComment: newVolunteer.teamWishComment, + } as VolunteerTeamWishes, + } + } +) + function getByEmail(list: Volunteer[], rawEmail: string): Volunteer | undefined { const email = canonicalEmail(rawEmail || "") const volunteer = list.find((v) => canonicalEmail(v.email) === email) diff --git a/src/server/index.ts b/src/server/index.ts index 42469cc..ec7d362 100755 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -21,10 +21,11 @@ import { javGameListGet } from "./gsheets/javGames" import { preVolunteerAdd, preVolunteerCountGet } from "./gsheets/preVolunteers" import { teamListGet } from "./gsheets/teams" import { - volunteerNotifsSet, volunteerSet, volunteerLogin, volunteerForgot, + volunteerNotifsSet, + volunteerTeamWishesSet, } from "./gsheets/volunteers" import { wishListGet, wishAdd } from "./gsheets/wishes" import config from "../config" @@ -75,6 +76,7 @@ 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("/VolunteerTeamWishesSet", secure as RequestHandler, volunteerTeamWishesSet) // Push notification subscription app.post("/notifications/subscribe", notificationsSubscribe) diff --git a/src/services/volunteers.ts b/src/services/volunteers.ts index 9956420..dff643a 100644 --- a/src/services/volunteers.ts +++ b/src/services/volunteers.ts @@ -21,6 +21,12 @@ export class Volunteer { active = "" + participingDays = [] + + teamWishes: string[] = [] + + teamWishComment = "" + hiddenNotifs: number[] = [] created = new Date() @@ -45,6 +51,9 @@ export const translationVolunteer: { [k in keyof Volunteer]: string } = { adult: "majeur", privileges: "privilege", active: "actif", + participingDays: "joursPrésent", + teamWishes: "enviesEquipe", + teamWishComment: "commentaireEnviesEquipe", hiddenNotifs: "notifsCachees", created: "creation", password1: "passe1", @@ -66,6 +75,9 @@ export const volunteerExample: Volunteer = { adult: 1, privileges: 0, active: "inconnu", + participingDays: [], + teamWishes: [], + teamWishComment: "", hiddenNotifs: [], created: new Date(0), password1: "$2y$10$fSxY9AIuxSiEjwF.J3eXGubIxUPkdq9d5fqpbl8ASimSjNj4SR.9O", @@ -110,3 +122,11 @@ export interface VolunteerNotifs { } export const volunteerNotifsSet = serviceAccessors.securedCustomPost<[number, Partial]>("NotifsSet") + +export interface VolunteerTeamWishes { + id: number + teamWishes: string[] + teamWishComment: string +} +export const volunteerTeamWishesSet = + serviceAccessors.securedCustomPost<[number, Partial]>("TeamWishesSet") diff --git a/src/store/rootReducer.ts b/src/store/rootReducer.ts index ca3b0bc..be064ba 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 volunteerTeamWishesSet from "./volunteerTeamWishesSet" import wishAdd from "./wishAdd" import wishList from "./wishList" @@ -31,6 +32,7 @@ export default (history: History) => ({ volunteerLogin, volunteerForgot, volunteerNotifsSet, + volunteerTeamWishesSet, wishAdd, wishList, router: connectRouter(history) as any, diff --git a/src/store/volunteerTeamWishesSet.ts b/src/store/volunteerTeamWishesSet.ts new file mode 100644 index 0000000..a4b0f1e --- /dev/null +++ b/src/store/volunteerTeamWishesSet.ts @@ -0,0 +1,58 @@ +import { PayloadAction, createSlice } from "@reduxjs/toolkit" + +import { StateRequest, toastError, elementFetch } from "./utils" +import { VolunteerTeamWishes, volunteerTeamWishesSet } from "../services/volunteers" +import { AppThunk, AppState } from "." + +type StateVolunteerTeamWishesSet = { entity?: VolunteerTeamWishes } & StateRequest + +export const initialState: StateVolunteerTeamWishesSet = { + readyStatus: "idle", +} + +const volunteerTeamWishesSetSlice = createSlice({ + name: "volunteerTeamWishesSet", + initialState, + reducers: { + getRequesting: (_) => ({ + readyStatus: "request", + }), + getSuccess: (_, { payload }: PayloadAction) => ({ + readyStatus: "success", + entity: payload, + }), + getFailure: (_, { payload }: PayloadAction) => ({ + readyStatus: "failure", + error: payload, + }), + }, +}) + +export default volunteerTeamWishesSetSlice.reducer +export const { getRequesting, getSuccess, getFailure } = volunteerTeamWishesSetSlice.actions + +export const fetchVolunteerTeamWishesSet = elementFetch( + volunteerTeamWishesSet, + getRequesting, + getSuccess, + getFailure, + (error: Error) => toastError(`Erreur lors du chargement des notifications: ${error.message}`) +) + +const shouldFetchVolunteerTeamWishesSet = (state: AppState, id: number) => + state.volunteerTeamWishesSet?.readyStatus !== "success" || + (state.volunteerTeamWishesSet?.entity && state.volunteerTeamWishesSet?.entity?.id !== id) + +export const fetchVolunteerTeamWishesSetIfNeed = + (id = 0, wishes: Partial = {}): AppThunk => + (dispatch, getState) => { + let jwt = "" + + if (!id) { + ;({ id, jwt } = getState().auth) + } + if (shouldFetchVolunteerTeamWishesSet(getState(), id)) + return dispatch(fetchVolunteerTeamWishesSet(jwt, id, wishes)) + + return null + }