mirror of
https://github.com/Paris-est-Ludique/intranet.git
synced 2025-09-11 05:46:28 +02:00
Add Ask Discord
This commit is contained in:
67
src/components/Asks/AskDiscord.tsx
Normal file
67
src/components/Asks/AskDiscord.tsx
Normal file
@@ -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,
|
||||||
|
<div className={styles.formLine}>
|
||||||
|
<p>
|
||||||
|
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 !<br />
|
||||||
|
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.
|
||||||
|
<br />
|
||||||
|
Pour rejoindre le serveur PeL, voici le lien d'invitation à cliquer :{" "}
|
||||||
|
<a href="https://discord.gg/eXhjKxSBB4" onClick={onSubmit}>
|
||||||
|
https://discord.gg/eXhjKxSBB4
|
||||||
|
</a>{" "}
|
||||||
|
!
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Prends le temps de le rejoindre maintenant, c'est via cet outil que la plupart des
|
||||||
|
équipes s'organisent !
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
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”.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className={styles.formButtons}>
|
||||||
|
<FormButton onClick={onSubmit}>Ok, noté</FormButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch server-side data here
|
||||||
|
export const fetchFor = [fetchVolunteerDiscordIdIfNeed]
|
@@ -191,7 +191,7 @@ export function AskPushNotif(asks: JSX.Element[], id: number): void {
|
|||||||
volunteerAsks,
|
volunteerAsks,
|
||||||
true,
|
true,
|
||||||
needToShow,
|
needToShow,
|
||||||
<div className={styles.formLine} key="line-participation">
|
<div className={styles.formLine}>
|
||||||
<label>
|
<label>
|
||||||
Acceptes-tu de recevoir une alerte dans ton navigateur quand on en aura
|
Acceptes-tu de recevoir une alerte dans ton navigateur quand on en aura
|
||||||
d'autres à t'afficher ici ?<br />
|
d'autres à t'afficher ici ?<br />
|
||||||
|
@@ -3,19 +3,21 @@ import React, { memo } from "react"
|
|||||||
import styles from "./styles.module.scss"
|
import styles from "./styles.module.scss"
|
||||||
import { useAskTools } from "./utils"
|
import { useAskTools } from "./utils"
|
||||||
import { AskWelcome } from "./AskWelcome"
|
import { AskWelcome } from "./AskWelcome"
|
||||||
import { AskPushNotif } from "./AskPushNotif"
|
import { AskDiscord, fetchFor as fetchForDiscord } from "./AskDiscord"
|
||||||
import { AskDayWishes, fetchFor as fetchForDayWishes } from "./AskDayWishes"
|
import { AskDayWishes, fetchFor as fetchForDayWishes } from "./AskDayWishes"
|
||||||
import { AskTeamWishes, fetchFor as fetchForTeamWishes } from "./AskTeamWishes"
|
import { AskTeamWishes, fetchFor as fetchForTeamWishes } from "./AskTeamWishes"
|
||||||
import {
|
import {
|
||||||
AskParticipationDetails,
|
AskParticipationDetails,
|
||||||
fetchFor as fetchForParticipationDetails,
|
fetchFor as fetchForParticipationDetails,
|
||||||
} from "./AskParticipationDetails"
|
} from "./AskParticipationDetails"
|
||||||
|
import { AskPushNotif } from "./AskPushNotif"
|
||||||
|
|
||||||
const Asks = (): JSX.Element | null => {
|
const Asks = (): JSX.Element | null => {
|
||||||
const { volunteerAsks } = useAskTools()
|
const { volunteerAsks } = useAskTools()
|
||||||
const asks: JSX.Element[] = []
|
const asks: JSX.Element[] = []
|
||||||
|
|
||||||
AskWelcome(asks, 1)
|
AskWelcome(asks, 1)
|
||||||
|
AskDiscord(asks, 3)
|
||||||
|
|
||||||
AskDayWishes(asks, 10)
|
AskDayWishes(asks, 10)
|
||||||
AskTeamWishes(asks, 11)
|
AskTeamWishes(asks, 11)
|
||||||
@@ -28,7 +30,7 @@ const Asks = (): JSX.Element | null => {
|
|||||||
<div key="pushNotifs">
|
<div key="pushNotifs">
|
||||||
<div className={styles.notificationsPage}>
|
<div className={styles.notificationsPage}>
|
||||||
<div className={styles.notificationsContent}>
|
<div className={styles.notificationsContent}>
|
||||||
<div className={styles.formLine} key="line-participation">
|
<div className={styles.formLine}>
|
||||||
<label>
|
<label>
|
||||||
Tu as fait le tour des dernières infos ou questions importantes,
|
Tu as fait le tour des dernières infos ou questions importantes,
|
||||||
merci ! :)
|
merci ! :)
|
||||||
@@ -54,6 +56,7 @@ export default memo(Asks)
|
|||||||
|
|
||||||
// Fetch server-side data here
|
// Fetch server-side data here
|
||||||
export const fetchFor = [
|
export const fetchFor = [
|
||||||
|
...fetchForDiscord,
|
||||||
...fetchForDayWishes,
|
...fetchForDayWishes,
|
||||||
...fetchForTeamWishes,
|
...fetchForTeamWishes,
|
||||||
...fetchForParticipationDetails,
|
...fetchForParticipationDetails,
|
||||||
|
@@ -34,7 +34,7 @@ export function addAsk(
|
|||||||
volunteerAsks: VolunteerAsks | undefined,
|
volunteerAsks: VolunteerAsks | undefined,
|
||||||
isNarrow: boolean,
|
isNarrow: boolean,
|
||||||
needToShow: boolean,
|
needToShow: boolean,
|
||||||
children: JSX.Element
|
children: JSX.Element | undefined
|
||||||
): void {
|
): void {
|
||||||
const hidden = volunteerAsks?.hiddenAsks || []
|
const hidden = volunteerAsks?.hiddenAsks || []
|
||||||
if (_.includes(hidden, id) || !_.isEmpty(asks) || !needToShow) {
|
if (_.includes(hidden, id) || !_.isEmpty(asks) || !needToShow) {
|
||||||
|
@@ -778,7 +778,7 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
|
|||||||
{meeting}
|
{meeting}
|
||||||
{helpBefore}
|
{helpBefore}
|
||||||
{pelMemberQuestion}
|
{pelMemberQuestion}
|
||||||
{pelMember && (
|
{(potentialVolunteer || pelMember) && (
|
||||||
<>
|
<>
|
||||||
{nameMobileEmail}
|
{nameMobileEmail}
|
||||||
{howToContact !== "Aucun" && submitButton}
|
{howToContact !== "Aucun" && submitButton}
|
||||||
|
@@ -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 <p>Loading...</p>
|
|
||||||
|
|
||||||
if (volunteerInfo.readyStatus === "failure" || !volunteerInfo.entity)
|
|
||||||
return <p>Oops! Failed to load data.</p>
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<VolunteerInfo item={volunteerInfo.entity} />
|
|
||||||
<VolunteerSet dispatch={dispatch} volunteer={volunteerInfo.entity} />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.VolunteerPage}>
|
|
||||||
<Helmet title="User Info" />
|
|
||||||
{renderInfo()}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LoadDataArgs {
|
|
||||||
params: { id: number }
|
|
||||||
}
|
|
||||||
|
|
||||||
export const loadData = ({ params }: LoadDataArgs): AppThunk[] => [fetchVolunteerIfNeed(params.id)]
|
|
||||||
|
|
||||||
export default memo(VolunteerPage)
|
|
@@ -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: <Loading />,
|
|
||||||
})
|
|
||||||
|
|
||||||
export default (props: Props): JSX.Element => (
|
|
||||||
<ErrorBoundary>
|
|
||||||
<VolunteerPage {...props} />
|
|
||||||
</ErrorBoundary>
|
|
||||||
)
|
|
||||||
export { loadData }
|
|
@@ -1,3 +0,0 @@
|
|||||||
.VolunteerPage {
|
|
||||||
padding: 0 15px;
|
|
||||||
}
|
|
@@ -8,7 +8,6 @@ import AsyncTeams, { loadData as loadTeamsData } from "../pages/Teams"
|
|||||||
import AsyncBoard, { loadData as loadBoardData } from "../pages/Board"
|
import AsyncBoard, { loadData as loadBoardData } from "../pages/Board"
|
||||||
import AsyncVolunteers, { loadData as loadVolunteersData } from "../pages/Volunteers"
|
import AsyncVolunteers, { loadData as loadVolunteersData } from "../pages/Volunteers"
|
||||||
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 Login from "../pages/Login"
|
import Login from "../pages/Login"
|
||||||
import Forgot from "../pages/Forgot"
|
import Forgot from "../pages/Forgot"
|
||||||
import NotFound from "../pages/NotFound"
|
import NotFound from "../pages/NotFound"
|
||||||
@@ -33,11 +32,6 @@ export default [
|
|||||||
component: AsyncRegisterPage,
|
component: AsyncRegisterPage,
|
||||||
loadData: loadRegisterPage,
|
loadData: loadRegisterPage,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "/VolunteerPage/:id",
|
|
||||||
component: AsyncVolunteerPage,
|
|
||||||
loadData: loadVolunteerPageData,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "/login",
|
path: "/login",
|
||||||
component: Login,
|
component: Login,
|
||||||
|
@@ -23,9 +23,20 @@ const expressAccessor = new ExpressAccessors<VolunteerWithoutId, Volunteer>(
|
|||||||
)
|
)
|
||||||
|
|
||||||
export const volunteerListGet = expressAccessor.listGet()
|
export const volunteerListGet = expressAccessor.listGet()
|
||||||
// export const volunteerAdd = expressAccessor.add()
|
|
||||||
export const volunteerSet = expressAccessor.set()
|
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) => {
|
export const volunteerPartialAdd = expressAccessor.add(async (list, body) => {
|
||||||
const params = body[0]
|
const params = body[0]
|
||||||
const volunteer = getByEmail(list, params.email)
|
const volunteer = getByEmail(list, params.email)
|
||||||
|
@@ -22,10 +22,11 @@ import { gameListGet } from "./gsheets/games"
|
|||||||
import { postulantAdd } from "./gsheets/postulants"
|
import { postulantAdd } from "./gsheets/postulants"
|
||||||
import { teamListGet } from "./gsheets/teams"
|
import { teamListGet } from "./gsheets/teams"
|
||||||
import {
|
import {
|
||||||
|
volunteerAsksSet,
|
||||||
volunteerDayWishesSet,
|
volunteerDayWishesSet,
|
||||||
volunteerForgot,
|
volunteerForgot,
|
||||||
|
volunteerDiscordId,
|
||||||
volunteerLogin,
|
volunteerLogin,
|
||||||
volunteerAsksSet,
|
|
||||||
volunteerPartialAdd,
|
volunteerPartialAdd,
|
||||||
volunteerParticipationDetailsSet,
|
volunteerParticipationDetailsSet,
|
||||||
volunteerSet,
|
volunteerSet,
|
||||||
@@ -92,7 +93,7 @@ app.post("/VolunteerForgot", volunteerForgot)
|
|||||||
app.get("/AnnouncementListGet", secure as RequestHandler, announcementListGet)
|
app.get("/AnnouncementListGet", secure as RequestHandler, announcementListGet)
|
||||||
app.post("/VolunteerSet", secure as RequestHandler, volunteerSet)
|
app.post("/VolunteerSet", secure as RequestHandler, volunteerSet)
|
||||||
app.get("/TeamListGet", teamListGet)
|
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("/VolunteerAsksSet", secure as RequestHandler, volunteerAsksSet)
|
||||||
app.post(
|
app.post(
|
||||||
"/VolunteerParticipationDetailsSet",
|
"/VolunteerParticipationDetailsSet",
|
||||||
|
@@ -64,7 +64,7 @@ export default class ServiceAccessors<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
secureListGet(): (jwt: string) => Promise<{
|
securedListGet(): (jwt: string) => Promise<{
|
||||||
data?: Element[]
|
data?: Element[]
|
||||||
error?: Error
|
error?: Error
|
||||||
}> {
|
}> {
|
||||||
@@ -192,6 +192,37 @@ export default class ServiceAccessors<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
securedCustomGet<InputElements extends Array<any>>(
|
||||||
|
apiName: string
|
||||||
|
): (
|
||||||
|
jwt: string,
|
||||||
|
...params: InputElements
|
||||||
|
) => Promise<{
|
||||||
|
data?: any
|
||||||
|
error?: Error
|
||||||
|
}> {
|
||||||
|
interface ElementGetResponse {
|
||||||
|
data?: any
|
||||||
|
error?: Error
|
||||||
|
}
|
||||||
|
return async (jwt: string, ...params: InputElements): Promise<ElementGetResponse> => {
|
||||||
|
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<InputElements extends Array<any>>(
|
securedCustomPost<InputElements extends Array<any>>(
|
||||||
apiName: string
|
apiName: string
|
||||||
): (
|
): (
|
||||||
|
@@ -7,4 +7,4 @@ const serviceAccessors = new ServiceAccessors<AnnouncementWithoutId, Announcemen
|
|||||||
// export const announcementAdd = serviceAccessors.add()
|
// export const announcementAdd = serviceAccessors.add()
|
||||||
// export const announcementSet = serviceAccessors.set()
|
// export const announcementSet = serviceAccessors.set()
|
||||||
|
|
||||||
export const announcementListGet = serviceAccessors.secureListGet()
|
export const announcementListGet = serviceAccessors.securedListGet()
|
||||||
|
@@ -139,6 +139,11 @@ export interface VolunteerForgot {
|
|||||||
message: string
|
message: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface VolunteerDiscordId {
|
||||||
|
id: Volunteer["id"]
|
||||||
|
discordId: Volunteer["discordId"]
|
||||||
|
}
|
||||||
|
|
||||||
export interface VolunteerAsks {
|
export interface VolunteerAsks {
|
||||||
id: Volunteer["id"]
|
id: Volunteer["id"]
|
||||||
firstname: Volunteer["firstname"]
|
firstname: Volunteer["firstname"]
|
||||||
|
@@ -12,7 +12,7 @@ import {
|
|||||||
const serviceAccessors = new ServiceAccessors<VolunteerWithoutId, Volunteer>(elementName)
|
const serviceAccessors = new ServiceAccessors<VolunteerWithoutId, Volunteer>(elementName)
|
||||||
|
|
||||||
export const volunteerListGet = serviceAccessors.listGet()
|
export const volunteerListGet = serviceAccessors.listGet()
|
||||||
export const volunteerGet = serviceAccessors.get()
|
export const volunteerDiscordIdGet = serviceAccessors.securedCustomGet<[number]>("DiscordId")
|
||||||
export const volunteerPartialAdd = serviceAccessors.customPost<[Partial<Volunteer>]>("PartialAdd")
|
export const volunteerPartialAdd = serviceAccessors.customPost<[Partial<Volunteer>]>("PartialAdd")
|
||||||
export const volunteerSet = serviceAccessors.set()
|
export const volunteerSet = serviceAccessors.set()
|
||||||
|
|
||||||
|
@@ -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)
|
|
||||||
})
|
|
||||||
})
|
|
@@ -35,9 +35,9 @@ export const auth = createSlice({
|
|||||||
|
|
||||||
export const { setCurrentUser, logoutUser } = auth.actions
|
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)
|
export const selectUserJwtToken = createSelector(selectAuthData, (authData) => authData.jwt)
|
||||||
|
|
||||||
|
@@ -7,8 +7,8 @@ import announcementList from "./announcementList"
|
|||||||
import postulantAdd from "./postulantAdd"
|
import postulantAdd from "./postulantAdd"
|
||||||
import teamList from "./teamList"
|
import teamList from "./teamList"
|
||||||
import ui from "./ui"
|
import ui from "./ui"
|
||||||
import volunteer from "./volunteer"
|
|
||||||
import volunteerAdd from "./volunteerPartialAdd"
|
import volunteerAdd from "./volunteerPartialAdd"
|
||||||
|
import volunteerDiscordId from "./volunteerDiscordId"
|
||||||
import volunteerList from "./volunteerList"
|
import volunteerList from "./volunteerList"
|
||||||
import volunteerSet from "./volunteerSet"
|
import volunteerSet from "./volunteerSet"
|
||||||
import volunteerLogin from "./volunteerLogin"
|
import volunteerLogin from "./volunteerLogin"
|
||||||
@@ -29,8 +29,8 @@ export default (history: History) => ({
|
|||||||
postulantAdd,
|
postulantAdd,
|
||||||
teamList,
|
teamList,
|
||||||
ui,
|
ui,
|
||||||
volunteer,
|
|
||||||
volunteerAdd,
|
volunteerAdd,
|
||||||
|
volunteerDiscordId,
|
||||||
volunteerList,
|
volunteerList,
|
||||||
volunteerSet,
|
volunteerSet,
|
||||||
volunteerLogin,
|
volunteerLogin,
|
||||||
|
@@ -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
|
|
||||||
}
|
|
65
src/store/volunteerDiscordId.ts
Normal file
65
src/store/volunteerDiscordId.ts
Normal 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
|
||||||
|
)
|
Reference in New Issue
Block a user