diff --git a/src/components/Asks/AskDiscord.tsx b/src/components/Asks/AskDiscord.tsx
new file mode 100644
index 0000000..0b23cec
--- /dev/null
+++ b/src/components/Asks/AskDiscord.tsx
@@ -0,0 +1,67 @@
+import { useCallback } from "react"
+import { useSelector } from "react-redux"
+import { fetchVolunteerAsksSet } from "../../store/volunteerAsksSet"
+import styles from "./styles.module.scss"
+import { useAskTools, addAsk } from "./utils"
+import FormButton from "../Form/FormButton/FormButton"
+import {
+ fetchVolunteerDiscordIdIfNeed,
+ selectVolunteerDiscordId,
+} from "../../store/volunteerDiscordId"
+
+export function AskDiscord(asks: JSX.Element[], id: number): void {
+ const { dispatch, jwtToken, volunteerAsks } = useAskTools()
+ const discordId: number | undefined = useSelector(selectVolunteerDiscordId)
+
+ const onSubmit = useCallback((): void => {
+ dispatch(
+ fetchVolunteerAsksSet(jwtToken, 0, {
+ hiddenAsks: [...(volunteerAsks?.hiddenAsks || []), id],
+ })
+ )
+ }, [dispatch, id, jwtToken, volunteerAsks?.hiddenAsks])
+
+ const needToShow = !discordId
+
+ addAsk(
+ asks,
+ id,
+ volunteerAsks,
+ true,
+ needToShow,
+
+
+ Discord nous permet gratuitement et sans pub de s'écrire entre bénévoles via nos
+ navigateurs ou smartphones. Et donc de s'organiser super efficacement !
+ C'est un peu déroutant au début, mais extrêmement pratique car à chaque sujet de
+ discussion correspond un salon différent que tu peux demander à suivre ou ignorer
+ totalement via la gestion des notifications.
+
+ Pour rejoindre le serveur PeL, voici le lien d'invitation à cliquer :{" "}
+
+ https://discord.gg/eXhjKxSBB4
+ {" "}
+ !
+
+
+ Prends le temps de le rejoindre maintenant, c'est via cet outil que la plupart des
+ équipes s'organisent !
+
+
+ Pour s'y retrouver tellement on est nombreux (plus de 120), il est nécessaire
+ d'avoir son prénom comme alias. Voir même d'avoir ensuite la première lettre de ton
+ nom de famille si un autre bénévole présent sur le serveur a le même prénom. Pour
+ changer ton alias uniquement sur le serveur PeL, il faut faire un clique droit sur
+ l'icône ronde du serveur en haut à gauche, et aller dans “Modifier le profil du
+ serveur”.
+
+
+
+ Ok, noté
+
+
+ )
+}
+
+// Fetch server-side data here
+export const fetchFor = [fetchVolunteerDiscordIdIfNeed]
diff --git a/src/components/Asks/AskPushNotif.tsx b/src/components/Asks/AskPushNotif.tsx
index 9676c19..7c963b9 100644
--- a/src/components/Asks/AskPushNotif.tsx
+++ b/src/components/Asks/AskPushNotif.tsx
@@ -191,7 +191,7 @@ export function AskPushNotif(asks: JSX.Element[], id: number): void {
volunteerAsks,
true,
needToShow,
-
+
Acceptes-tu de recevoir une alerte dans ton navigateur quand on en aura
d'autres à t'afficher ici ?
diff --git a/src/components/Asks/index.tsx b/src/components/Asks/index.tsx
index e4877d5..14428e1 100644
--- a/src/components/Asks/index.tsx
+++ b/src/components/Asks/index.tsx
@@ -3,19 +3,21 @@ import React, { memo } from "react"
import styles from "./styles.module.scss"
import { useAskTools } from "./utils"
import { AskWelcome } from "./AskWelcome"
-import { AskPushNotif } from "./AskPushNotif"
+import { AskDiscord, fetchFor as fetchForDiscord } from "./AskDiscord"
import { AskDayWishes, fetchFor as fetchForDayWishes } from "./AskDayWishes"
import { AskTeamWishes, fetchFor as fetchForTeamWishes } from "./AskTeamWishes"
import {
AskParticipationDetails,
fetchFor as fetchForParticipationDetails,
} from "./AskParticipationDetails"
+import { AskPushNotif } from "./AskPushNotif"
const Asks = (): JSX.Element | null => {
const { volunteerAsks } = useAskTools()
const asks: JSX.Element[] = []
AskWelcome(asks, 1)
+ AskDiscord(asks, 3)
AskDayWishes(asks, 10)
AskTeamWishes(asks, 11)
@@ -28,7 +30,7 @@ const Asks = (): JSX.Element | null => {
-
+
Tu as fait le tour des dernières infos ou questions importantes,
merci ! :)
@@ -54,6 +56,7 @@ export default memo(Asks)
// Fetch server-side data here
export const fetchFor = [
+ ...fetchForDiscord,
...fetchForDayWishes,
...fetchForTeamWishes,
...fetchForParticipationDetails,
diff --git a/src/components/Asks/utils.tsx b/src/components/Asks/utils.tsx
index aca7104..a2644f2 100644
--- a/src/components/Asks/utils.tsx
+++ b/src/components/Asks/utils.tsx
@@ -34,7 +34,7 @@ export function addAsk(
volunteerAsks: VolunteerAsks | undefined,
isNarrow: boolean,
needToShow: boolean,
- children: JSX.Element
+ children: JSX.Element | undefined
): void {
const hidden = volunteerAsks?.hiddenAsks || []
if (_.includes(hidden, id) || !_.isEmpty(asks) || !needToShow) {
diff --git a/src/components/RegisterForm/index.tsx b/src/components/RegisterForm/index.tsx
index 54223d0..6fc1ace 100644
--- a/src/components/RegisterForm/index.tsx
+++ b/src/components/RegisterForm/index.tsx
@@ -778,7 +778,7 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
{meeting}
{helpBefore}
{pelMemberQuestion}
- {pelMember && (
+ {(potentialVolunteer || pelMember) && (
<>
{nameMobileEmail}
{howToContact !== "Aucun" && submitButton}
diff --git a/src/pages/VolunteerPage/VolunteerPage.tsx b/src/pages/VolunteerPage/VolunteerPage.tsx
deleted file mode 100755
index d40a438..0000000
--- a/src/pages/VolunteerPage/VolunteerPage.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import { useEffect, memo } from "react"
-import { RouteComponentProps } from "react-router-dom"
-import { useDispatch, useSelector, shallowEqual } from "react-redux"
-import { Helmet } from "react-helmet"
-
-import { AppState, AppThunk } from "../../store"
-import { fetchVolunteerIfNeed } from "../../store/volunteer"
-import { VolunteerInfo, VolunteerSet } from "../../components"
-import styles from "./styles.module.scss"
-
-export type Props = RouteComponentProps<{ id: string }>
-
-const VolunteerPage = ({ match }: Props): JSX.Element => {
- const { id: rawId } = match.params
- const id = +rawId
- const dispatch = useDispatch()
- const volunteer = useSelector((state: AppState) => state.volunteer, shallowEqual)
-
- useEffect(() => {
- dispatch(fetchVolunteerIfNeed(id))
- }, [dispatch, id])
-
- const renderInfo = () => {
- const volunteerInfo = volunteer
-
- if (!volunteerInfo || volunteerInfo.readyStatus === "request") return Loading...
-
- if (volunteerInfo.readyStatus === "failure" || !volunteerInfo.entity)
- return Oops! Failed to load data.
-
- return (
-
-
-
-
- )
- }
-
- return (
-
-
- {renderInfo()}
-
- )
-}
-
-interface LoadDataArgs {
- params: { id: number }
-}
-
-export const loadData = ({ params }: LoadDataArgs): AppThunk[] => [fetchVolunteerIfNeed(params.id)]
-
-export default memo(VolunteerPage)
diff --git a/src/pages/VolunteerPage/index.tsx b/src/pages/VolunteerPage/index.tsx
deleted file mode 100755
index a871900..0000000
--- a/src/pages/VolunteerPage/index.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import loadable from "@loadable/component"
-
-import { Loading, ErrorBoundary } from "../../components"
-import { Props, loadData } from "./VolunteerPage"
-
-const VolunteerPage = loadable(() => import("./VolunteerPage"), {
- fallback: ,
-})
-
-export default (props: Props): JSX.Element => (
-
-
-
-)
-export { loadData }
diff --git a/src/pages/VolunteerPage/styles.module.scss b/src/pages/VolunteerPage/styles.module.scss
deleted file mode 100755
index b95a81a..0000000
--- a/src/pages/VolunteerPage/styles.module.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-.VolunteerPage {
- padding: 0 15px;
-}
diff --git a/src/routes/index.ts b/src/routes/index.ts
index 9eb2b1d..ceb2d08 100755
--- a/src/routes/index.ts
+++ b/src/routes/index.ts
@@ -8,7 +8,6 @@ import AsyncTeams, { loadData as loadTeamsData } from "../pages/Teams"
import AsyncBoard, { loadData as loadBoardData } from "../pages/Board"
import AsyncVolunteers, { loadData as loadVolunteersData } from "../pages/Volunteers"
import AsyncWish, { loadData as loadWishData } from "../pages/Wish"
-import AsyncVolunteerPage, { loadData as loadVolunteerPageData } from "../pages/VolunteerPage"
import Login from "../pages/Login"
import Forgot from "../pages/Forgot"
import NotFound from "../pages/NotFound"
@@ -33,11 +32,6 @@ export default [
component: AsyncRegisterPage,
loadData: loadRegisterPage,
},
- {
- path: "/VolunteerPage/:id",
- component: AsyncVolunteerPage,
- loadData: loadVolunteerPageData,
- },
{
path: "/login",
component: Login,
diff --git a/src/server/gsheets/volunteers.ts b/src/server/gsheets/volunteers.ts
index e31ef76..bc43a7c 100644
--- a/src/server/gsheets/volunteers.ts
+++ b/src/server/gsheets/volunteers.ts
@@ -23,9 +23,20 @@ const expressAccessor = new ExpressAccessors(
)
export const volunteerListGet = expressAccessor.listGet()
-// export const volunteerAdd = expressAccessor.add()
export const volunteerSet = expressAccessor.set()
+export const volunteerDiscordId = expressAccessor.get(async (list, body, id) => {
+ const requestedId = +body[0] || id
+ if (requestedId !== id && requestedId !== 0) {
+ throw Error(`On ne peut acceder qu'à ses propres envies de jours`)
+ }
+ const volunteer = list.find((v) => v.id === requestedId)
+ if (!volunteer) {
+ throw Error(`Il n'y a aucun bénévole avec cet identifiant ${requestedId}`)
+ }
+ return _.pick(volunteer, "id", "discordId")
+})
+
export const volunteerPartialAdd = expressAccessor.add(async (list, body) => {
const params = body[0]
const volunteer = getByEmail(list, params.email)
diff --git a/src/server/index.ts b/src/server/index.ts
index 67062d5..dc45cdf 100755
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -22,10 +22,11 @@ import { gameListGet } from "./gsheets/games"
import { postulantAdd } from "./gsheets/postulants"
import { teamListGet } from "./gsheets/teams"
import {
+ volunteerAsksSet,
volunteerDayWishesSet,
volunteerForgot,
+ volunteerDiscordId,
volunteerLogin,
- volunteerAsksSet,
volunteerPartialAdd,
volunteerParticipationDetailsSet,
volunteerSet,
@@ -92,7 +93,7 @@ app.post("/VolunteerForgot", volunteerForgot)
app.get("/AnnouncementListGet", secure as RequestHandler, announcementListGet)
app.post("/VolunteerSet", secure as RequestHandler, volunteerSet)
app.get("/TeamListGet", teamListGet)
-// UNSAFE app.post("/VolunteerGet", secure as RequestHandler, volunteerGet)
+app.get("/VolunteerDiscordId", secure as RequestHandler, volunteerDiscordId)
app.post("/VolunteerAsksSet", secure as RequestHandler, volunteerAsksSet)
app.post(
"/VolunteerParticipationDetailsSet",
diff --git a/src/services/accessors.ts b/src/services/accessors.ts
index e11579b..509795f 100644
--- a/src/services/accessors.ts
+++ b/src/services/accessors.ts
@@ -64,7 +64,7 @@ export default class ServiceAccessors<
}
}
- secureListGet(): (jwt: string) => Promise<{
+ securedListGet(): (jwt: string) => Promise<{
data?: Element[]
error?: Error
}> {
@@ -192,6 +192,37 @@ export default class ServiceAccessors<
}
}
+ securedCustomGet>(
+ apiName: string
+ ): (
+ jwt: string,
+ ...params: InputElements
+ ) => Promise<{
+ data?: any
+ error?: Error
+ }> {
+ interface ElementGetResponse {
+ data?: any
+ error?: Error
+ }
+ return async (jwt: string, ...params: InputElements): Promise => {
+ try {
+ const auth = { headers: { Authorization: `Bearer ${jwt}` } }
+ const fullAxiosConfig = _.defaultsDeep(auth, axiosConfig)
+ const { data } = await axios.get(
+ `${config.API_URL}/${this.elementName}${apiName}`,
+ { ...fullAxiosConfig, params }
+ )
+ if (data.error) {
+ throw Error(data.error)
+ }
+ return { data }
+ } catch (error) {
+ return { error: error as Error }
+ }
+ }
+ }
+
securedCustomPost>(
apiName: string
): (
diff --git a/src/services/announcementAccessors.ts b/src/services/announcementAccessors.ts
index 460cf42..4571e7f 100644
--- a/src/services/announcementAccessors.ts
+++ b/src/services/announcementAccessors.ts
@@ -7,4 +7,4 @@ const serviceAccessors = new ServiceAccessors(elementName)
export const volunteerListGet = serviceAccessors.listGet()
-export const volunteerGet = serviceAccessors.get()
+export const volunteerDiscordIdGet = serviceAccessors.securedCustomGet<[number]>("DiscordId")
export const volunteerPartialAdd = serviceAccessors.customPost<[Partial]>("PartialAdd")
export const volunteerSet = serviceAccessors.set()
diff --git a/src/store/__tests__/volunteer.ts b/src/store/__tests__/volunteer.ts
deleted file mode 100644
index 4a5f993..0000000
--- a/src/store/__tests__/volunteer.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import axios from "axios"
-
-import mockStore from "../../utils/mockStore"
-import volunteer, {
- getRequesting,
- getSuccess,
- getFailure,
- fetchVolunteer,
- initialState,
-} from "../volunteer"
-import { Volunteer, volunteerExample } from "../../services/volunteers"
-
-jest.mock("axios")
-
-const mockData: Volunteer = volunteerExample
-const { id } = mockData
-const mockError = "Oops! Something went wrong."
-
-describe("volunteer reducer", () => {
- it("should handle initial state correctly", () => {
- // @ts-expect-error
- expect(volunteer(undefined, {})).toEqual(initialState)
- })
-
- it("should handle requesting correctly", () => {
- expect(volunteer(undefined, { type: getRequesting.type, payload: id })).toEqual({
- readyStatus: "request",
- })
- })
-
- it("should handle success correctly", () => {
- expect(
- volunteer(undefined, {
- type: getSuccess.type,
- payload: mockData,
- })
- ).toEqual({ readyStatus: "success", entity: mockData })
- })
-
- it("should handle failure correctly", () => {
- expect(
- volunteer(undefined, {
- type: getFailure.type,
- payload: mockError,
- })
- ).toEqual({ readyStatus: "failure", error: mockError })
- })
-})
-
-describe("volunteer action", () => {
- it("fetches volunteer data successful", async () => {
- const { dispatch, getActions } = mockStore()
- const expectedActions = [
- { type: getRequesting.type, payload: undefined },
- { type: getSuccess.type, payload: mockData },
- ]
-
- // @ts-expect-error
- axios.get.mockResolvedValue({ data: mockData })
-
- await dispatch(fetchVolunteer(id))
- expect(getActions()).toEqual(expectedActions)
- })
-
- it("fetches volunteer data failed", async () => {
- const { dispatch, getActions } = mockStore()
- const expectedActions = [
- { type: getRequesting.type },
- { type: getFailure.type, payload: mockError },
- ]
-
- // @ts-expect-error
- axios.get.mockRejectedValue({ message: mockError })
-
- await dispatch(fetchVolunteer(id))
- expect(getActions()).toEqual(expectedActions)
- })
-})
diff --git a/src/store/auth.ts b/src/store/auth.ts
index d7dc99a..0f0f346 100644
--- a/src/store/auth.ts
+++ b/src/store/auth.ts
@@ -35,9 +35,9 @@ export const auth = createSlice({
export const { setCurrentUser, logoutUser } = auth.actions
-export const selectAuthData = (state: AppState): AuthState => state.auth
+const selectAuthData = (state: AppState): AuthState => state.auth
-export const selectRouter = (state: AppState): AppState["router"] => state.router
+const selectRouter = (state: AppState): AppState["router"] => state.router
export const selectUserJwtToken = createSelector(selectAuthData, (authData) => authData.jwt)
diff --git a/src/store/rootReducer.ts b/src/store/rootReducer.ts
index 49a5a98..f2ea28c 100644
--- a/src/store/rootReducer.ts
+++ b/src/store/rootReducer.ts
@@ -7,8 +7,8 @@ import announcementList from "./announcementList"
import postulantAdd from "./postulantAdd"
import teamList from "./teamList"
import ui from "./ui"
-import volunteer from "./volunteer"
import volunteerAdd from "./volunteerPartialAdd"
+import volunteerDiscordId from "./volunteerDiscordId"
import volunteerList from "./volunteerList"
import volunteerSet from "./volunteerSet"
import volunteerLogin from "./volunteerLogin"
@@ -29,8 +29,8 @@ export default (history: History) => ({
postulantAdd,
teamList,
ui,
- volunteer,
volunteerAdd,
+ volunteerDiscordId,
volunteerList,
volunteerSet,
volunteerLogin,
diff --git a/src/store/volunteer.ts b/src/store/volunteer.ts
deleted file mode 100644
index b883500..0000000
--- a/src/store/volunteer.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-import { PayloadAction, createSlice } from "@reduxjs/toolkit"
-
-import { StateRequest, toastError, elementFetch } from "./utils"
-import { Volunteer } from "../services/volunteers"
-import { AppThunk, AppState } from "."
-import { volunteerGet } from "../services/volunteersAccessors"
-
-type StateVolunteer = { entity?: Volunteer } & StateRequest
-
-export const initialState: StateVolunteer = {
- readyStatus: "idle",
-}
-
-const volunteer = createSlice({
- name: "volunteer",
- initialState,
- reducers: {
- getRequesting: (_) => ({
- readyStatus: "request",
- }),
- getSuccess: (_, { payload }: PayloadAction) => ({
- readyStatus: "success",
- entity: payload,
- }),
- getFailure: (_, { payload }: PayloadAction) => ({
- readyStatus: "failure",
- error: payload,
- }),
- },
-})
-
-export default volunteer.reducer
-export const { getRequesting, getSuccess, getFailure } = volunteer.actions
-
-export const fetchVolunteer = elementFetch(
- volunteerGet,
- getRequesting,
- getSuccess,
- getFailure,
- (error: Error) => toastError(`Erreur lors du chargement d'un bénévole: ${error.message}`)
-)
-
-const shouldFetchVolunteer = (state: AppState, id: number) =>
- state.volunteer.readyStatus !== "success" ||
- (state.volunteer.entity && state.volunteer.entity.id !== id)
-
-export const fetchVolunteerIfNeed =
- (id: number): AppThunk =>
- (dispatch, getState) => {
- if (shouldFetchVolunteer(getState(), id)) return dispatch(fetchVolunteer(id))
-
- return null
- }
diff --git a/src/store/volunteerDiscordId.ts b/src/store/volunteerDiscordId.ts
new file mode 100644
index 0000000..15fddc2
--- /dev/null
+++ b/src/store/volunteerDiscordId.ts
@@ -0,0 +1,65 @@
+import { PayloadAction, createSlice, createSelector } from "@reduxjs/toolkit"
+
+import { StateRequest, toastError, elementFetch } from "./utils"
+import { VolunteerDiscordId } from "../services/volunteers"
+import { AppThunk, AppState } from "."
+import { volunteerDiscordIdGet } from "../services/volunteersAccessors"
+
+type StateVolunteerDiscordId = { entity?: VolunteerDiscordId } & StateRequest
+
+export const initialState: StateVolunteerDiscordId = {
+ readyStatus: "idle",
+}
+
+const volunteerDiscordId = createSlice({
+ name: "volunteerDiscordId",
+ initialState,
+ reducers: {
+ getRequesting: (_) => ({
+ readyStatus: "request",
+ }),
+ getSuccess: (_, { payload }: PayloadAction) => ({
+ readyStatus: "success",
+ entity: payload,
+ }),
+ getFailure: (_, { payload }: PayloadAction) => ({
+ readyStatus: "failure",
+ error: payload,
+ }),
+ },
+})
+
+export default volunteerDiscordId.reducer
+export const { getRequesting, getSuccess, getFailure } = volunteerDiscordId.actions
+
+export const fetchVolunteerDiscordId = elementFetch(
+ volunteerDiscordIdGet,
+ getRequesting,
+ getSuccess,
+ getFailure,
+ (error: Error) =>
+ toastError(`Erreur lors du chargement du discordId d'un bénévole: ${error.message}`)
+)
+
+const shouldFetchVolunteerDiscordId = (state: AppState, id: number) =>
+ state.volunteerDiscordId.readyStatus !== "success" ||
+ (state.volunteerDiscordId.entity && state.volunteerDiscordId.entity.id !== id)
+
+export const fetchVolunteerDiscordIdIfNeed =
+ (id = 0): AppThunk =>
+ (dispatch, getState) => {
+ let jwt = ""
+
+ if (!id) {
+ ;({ jwt, id } = getState().auth)
+ }
+ if (shouldFetchVolunteerDiscordId(getState(), id))
+ return dispatch(fetchVolunteerDiscordId(jwt, id))
+
+ return null
+ }
+
+export const selectVolunteerDiscordId = createSelector(
+ (state: AppState) => state,
+ (state): number | undefined => state.volunteerDiscordId?.entity?.id
+)