Merge from registration branch which was in prod

This commit is contained in:
pikiou
2022-04-19 03:23:53 +02:00
50 changed files with 1584 additions and 820 deletions

View File

@@ -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)
})
})

View File

@@ -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)

39
src/store/postulantAdd.ts Normal file
View File

@@ -0,0 +1,39 @@
import { PayloadAction, createSlice, createEntityAdapter } from "@reduxjs/toolkit"
import { StateRequest, elementAddFetch } from "./utils"
import { Postulant } from "../services/postulants"
import { postulantAdd } from "../services/postulantsAccessors"
const postulantAdapter = createEntityAdapter<Postulant>()
const postulantAddSlice = createSlice({
name: "postulantAdd",
initialState: postulantAdapter.getInitialState({
readyStatus: "idle",
} as StateRequest),
reducers: {
getRequesting: (state) => {
state.readyStatus = "request"
},
getSuccess: (state, { payload }: PayloadAction<Postulant>) => {
state.readyStatus = "success"
postulantAdapter.setOne(state, payload)
},
getFailure: (state, { payload }: PayloadAction<string>) => {
state.readyStatus = "failure"
state.error = payload
},
},
})
export default postulantAddSlice.reducer
export const { getRequesting, getSuccess, getFailure } = postulantAddSlice.actions
export const fetchPostulantAdd = elementAddFetch(
postulantAdd,
getRequesting,
getSuccess,
getFailure,
() => null,
() => null
)

View File

@@ -1,39 +0,0 @@
import { PayloadAction, createSlice, createEntityAdapter } from "@reduxjs/toolkit"
import { StateRequest, elementAddFetch } from "./utils"
import { PreVolunteer } from "../services/preVolunteers"
import { preVolunteerAdd } from "../services/preVolunteersAccessors"
const preVolunteerAdapter = createEntityAdapter<PreVolunteer>()
const preVolunteerAddSlice = createSlice({
name: "addPreVolunteer",
initialState: preVolunteerAdapter.getInitialState({
readyStatus: "idle",
} as StateRequest),
reducers: {
getRequesting: (state) => {
state.readyStatus = "request"
},
getSuccess: (state, { payload }: PayloadAction<PreVolunteer>) => {
state.readyStatus = "success"
preVolunteerAdapter.addOne(state, payload)
},
getFailure: (state, { payload }: PayloadAction<string>) => {
state.readyStatus = "failure"
state.error = payload
},
},
})
export default preVolunteerAddSlice.reducer
export const { getRequesting, getSuccess, getFailure } = preVolunteerAddSlice.actions
export const fetchPreVolunteerAdd = elementAddFetch(
preVolunteerAdd,
getRequesting,
getSuccess,
getFailure,
() => null,
() => null
)

View File

@@ -1,46 +0,0 @@
import { PayloadAction, createSlice } from "@reduxjs/toolkit"
import { StateRequest, toastError, elementValueFetch } from "./utils"
import { preVolunteerCountGet } from "../services/preVolunteersAccessors"
import { AppThunk, AppState } from "."
export const initialState: StateRequest & { value?: number } = { readyStatus: "idle" }
const preVolunteerCount = createSlice({
name: "preVolunteerCount",
initialState,
reducers: {
getRequesting: (state) => {
state.readyStatus = "request"
},
getSuccess: (state, { payload }: PayloadAction<number>) => {
state.readyStatus = "success"
state.value = payload
},
getFailure: (state, { payload }: PayloadAction<string>) => {
state.readyStatus = "failure"
state.error = payload
},
},
})
export default preVolunteerCount.reducer
export const { getRequesting, getSuccess, getFailure } = preVolunteerCount.actions
export const fetchPreVolunteerCount = elementValueFetch(
preVolunteerCountGet,
getRequesting,
getSuccess,
getFailure,
(error: Error) =>
toastError(`Erreur lors du chargement des bénévoles potentiels: ${error.message}`)
)
const shouldFetchPreVolunteerCount = (state: AppState) =>
state.preVolunteerCount.readyStatus !== "success"
export const fetchPreVolunteerCountIfNeed = (): AppThunk => (dispatch, getState) => {
if (shouldFetchPreVolunteerCount(getState())) return dispatch(fetchPreVolunteerCount())
return null
}

View File

@@ -4,12 +4,11 @@ import { connectRouter } from "connected-react-router"
import auth from "./auth"
import gameList from "./gameList"
import announcementList from "./announcementList"
import preVolunteerAdd from "./preVolunteerAdd"
import preVolunteerCount from "./preVolunteerCount"
import postulantAdd from "./postulantAdd"
import teamList from "./teamList"
import ui from "./ui"
import volunteer from "./volunteer"
import volunteerAdd from "./volunteerAdd"
import volunteerAdd from "./volunteerPartialAdd"
import volunteerDiscordId from "./volunteerDiscordId"
import volunteerList from "./volunteerList"
import volunteerSet from "./volunteerSet"
import volunteerLogin from "./volunteerLogin"
@@ -28,12 +27,11 @@ export default (history: History) => ({
auth,
gameList,
announcementList,
preVolunteerAdd,
preVolunteerCount,
postulantAdd,
teamList,
ui,
volunteer,
volunteerAdd,
volunteerDiscordId,
volunteerList,
volunteerSet,
volunteerLogin,

View File

@@ -65,7 +65,7 @@ export function elementFetch<Element, ServiceInput extends Array<any>>(
}
export function elementAddFetch<Element>(
elementAddService: (volunteerWithoutId: Omit<Element, "id">) => Promise<{
elementAddService: (elementWithoutId: Omit<Element, "id">) => Promise<{
data?: Element | undefined
error?: Error | undefined
}>,
@@ -74,12 +74,12 @@ export function elementAddFetch<Element>(
getFailure: ActionCreatorWithPayload<string, string>,
errorMessage?: (error: Error) => void,
successMessage?: () => void
): (volunteerWithoutId: Omit<Element, "id">) => AppThunk {
return (volunteerWithoutId: Omit<Element, "id">): AppThunk =>
): (elementWithoutId: Omit<Element, "id">) => AppThunk {
return (elementWithoutId: Omit<Element, "id">): AppThunk =>
async (dispatch) => {
dispatch(getRequesting())
const { error, data } = await elementAddService(volunteerWithoutId)
const { error, data } = await elementAddService(elementWithoutId)
if (error) {
dispatch(getFailure(error.message))
@@ -119,7 +119,7 @@ export function elementListFetch<Element, ServiceInput extends Array<any>>(
}
export function elementSet<Element>(
elementSetService: (volunteer: Element) => Promise<{
elementSetService: (element: Element) => Promise<{
data?: Element | undefined
error?: Error | undefined
}>,

View File

@@ -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<Volunteer>) => ({
readyStatus: "success",
entity: payload,
}),
getFailure: (_, { payload }: PayloadAction<string>) => ({
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
}

View File

@@ -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<VolunteerDiscordId>) => ({
readyStatus: "success",
entity: payload,
}),
getFailure: (_, { payload }: PayloadAction<string>) => ({
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
)

View File

@@ -1,13 +1,13 @@
import { PayloadAction, createSlice, createEntityAdapter } from "@reduxjs/toolkit"
import { StateRequest, toastError, toastSuccess, elementAddFetch } from "./utils"
import { StateRequest, elementAddFetch } from "./utils"
import { Volunteer } from "../services/volunteers"
import { volunteerAdd } from "../services/volunteersAccessors"
import { volunteerPartialAdd } from "../services/volunteersAccessors"
const volunteerAdapter = createEntityAdapter<Volunteer>()
const volunteerAddSlice = createSlice({
name: "addVolunteer",
const volunteerPartialAddSlice = createSlice({
name: "volunteerAdd",
initialState: volunteerAdapter.getInitialState({
readyStatus: "idle",
} as StateRequest),
@@ -17,7 +17,7 @@ const volunteerAddSlice = createSlice({
},
getSuccess: (state, { payload }: PayloadAction<Volunteer>) => {
state.readyStatus = "success"
volunteerAdapter.addOne(state, payload)
volunteerAdapter.setOne(state, payload)
},
getFailure: (state, { payload }: PayloadAction<string>) => {
state.readyStatus = "failure"
@@ -26,14 +26,14 @@ const volunteerAddSlice = createSlice({
},
})
export default volunteerAddSlice.reducer
export const { getRequesting, getSuccess, getFailure } = volunteerAddSlice.actions
export default volunteerPartialAddSlice.reducer
export const { getRequesting, getSuccess, getFailure } = volunteerPartialAddSlice.actions
export const fetchVolunteerAdd = elementAddFetch(
volunteerAdd,
export const fetchVolunteerPartialAdd = elementAddFetch(
volunteerPartialAdd,
getRequesting,
getSuccess,
getFailure,
(error: Error) => toastError(`Erreur lors de l'ajout d'un bénévole: ${error.message}`),
() => toastSuccess("Volunteer ajoutée !")
() => null,
() => null
)

View File

@@ -7,7 +7,7 @@ import { wishAdd } from "../services/wishesAccessors"
const wishAdapter = createEntityAdapter<Wish>()
const wishAddSlice = createSlice({
name: "addWish",
name: "wishAdd",
initialState: wishAdapter.getInitialState({
readyStatus: "idle",
} as StateRequest),

View File

@@ -8,7 +8,7 @@ import { wishListGet } from "../services/wishesAccessors"
const wishAdapter = createEntityAdapter<Wish>()
const wishList = createSlice({
name: "getWishList",
name: "wishList",
initialState: wishAdapter.getInitialState({
readyStatus: "idle",
} as StateRequest),