Add teamList in store

This commit is contained in:
pikiou 2022-01-20 01:09:32 +01:00
parent 1011a293d0
commit d5eeb44d2f
10 changed files with 208 additions and 11 deletions

63
src/pages/Teams/Teams.tsx Normal file
View File

@ -0,0 +1,63 @@
import { FC, 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, EntitiesRequest } from "../../store"
import { Team } from "../../services/teams"
import { fetchTeamListIfNeed } from "../../store/teamList"
import styles from "./styles.module.scss"
export type Props = RouteComponentProps
function useList(
stateToProp: (state: AppState) => EntitiesRequest<Team>,
fetchDataIfNeed: () => AppThunk
) {
const dispatch = useDispatch()
const { ids, entities, readyStatus } = useSelector(stateToProp, shallowEqual)
// Fetch client-side data here
useEffect(() => {
dispatch(fetchDataIfNeed())
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dispatch])
return () => {
if (!readyStatus || readyStatus === "idle" || readyStatus === "request")
return <p>Loading...</p>
if (readyStatus === "failure") return <p>Oops, Failed to load!</p>
return ids.map((id) => {
const team = entities[id]
return team === undefined ? null : (
<div key={id}>
<b>{team.name}</b>:{team.description}
<br />
Avant: {team.before}
<br />
Pendant: {team.during}
<br />
Après: {team.after}
<br />
<br />
</div>
)
})
}
}
const TeamsPage: FC<Props> = (): JSX.Element => (
<div className={styles.teamsPage}>
<div className={styles.teamsContent}>
<Helmet title="TeamsPage" />
{useList((state: AppState) => state.teamList, fetchTeamListIfNeed)()}
</div>
</div>
)
// Fetch server-side data here
export const loadData = (): AppThunk[] => [fetchTeamListIfNeed()]
export default memo(TeamsPage)

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

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

View File

@ -0,0 +1,9 @@
@import "../../theme/mixins";
.teamsPage {
@include page-wrapper-center;
}
.teamsContent {
@include page-content-wrapper(600px);
}

View File

@ -3,6 +3,7 @@ import { RouteConfig } from "react-router-config"
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 AsyncWish, { loadData as loadWishData } from "../pages/Wish"
import AsyncVolunteerPage, { loadData as loadVolunteerPageData } from "../pages/VolunteerPage"
import Login from "../pages/Login"
@ -37,6 +38,11 @@ export default [
path: "/forgot",
component: Forgot,
},
{
path: "/teams",
component: AsyncTeams,
loadData: loadTeamsData,
},
{
path: "/wish",
component: AsyncWish,

View File

@ -21,10 +21,12 @@ export type ElementWithId<ElementNoId> = { id: number } & ElementNoId
export class SheetNames {
JavGames = "Jeux JAV"
Volunteers = "Membres"
PreVolunteers = "PreMembres"
Teams = "Equipes"
Volunteers = "Membres"
Wishes = "Envies d'aider"
}
export const sheetNames = new SheetNames()

View File

@ -0,0 +1,10 @@
import ExpressAccessors from "./expressAccessors"
import { Team, TeamWithoutId, translationTeam } from "../../services/teams"
const expressAccessor = new ExpressAccessors<TeamWithoutId, Team>(
"Teams",
new Team(),
translationTeam
)
export const teamListGet = expressAccessor.listGet()

View File

@ -18,14 +18,15 @@ import ssr from "./ssr"
import certbotRouter from "../routes/certbot"
import { secure } from "./secure"
import { javGameListGet } from "./gsheets/javGames"
import { wishListGet, wishAdd } from "./gsheets/wishes"
import { preVolunteerAdd, preVolunteerCountGet } from "./gsheets/preVolunteers"
import { teamListGet } from "./gsheets/teams"
import {
volunteerNotifsSet,
volunteerSet,
volunteerLogin,
volunteerForgot,
} from "./gsheets/volunteers"
import { wishListGet, wishAdd } from "./gsheets/wishes"
import config from "../config"
import notificationsSubscribe from "./notificationsSubscribe"
import checkAccess from "./checkAccess"
@ -71,6 +72,7 @@ app.post("/VolunteerForgot", volunteerForgot)
// Secured APIs
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)

39
src/services/teams.ts Normal file
View File

@ -0,0 +1,39 @@
import ServiceAccessors from "./accessors"
export class Team {
id = 0
name = ""
min = 0
max = 0
description = ""
before = ""
during = ""
after = ""
}
export const translationTeam: { [k in keyof Team]: string } = {
id: "id",
name: "nom",
min: "min",
max: "max",
description: "description",
before: "avant",
during: "pendant",
after: "après",
}
const elementName = "Team"
export type TeamWithoutId = Omit<Team, "id">
const serviceAccessors = new ServiceAccessors<TeamWithoutId, Team>(elementName)
export const teamListGet = serviceAccessors.listGet()
export const teamGet = serviceAccessors.get()

View File

@ -2,9 +2,10 @@ import { History } from "history"
import { connectRouter } from "connected-react-router"
import auth from "./auth"
import wishAdd from "./wishAdd"
import wishList from "./wishList"
import javGameList from "./javGameList"
import preVolunteerAdd from "./preVolunteerAdd"
import preVolunteerCount from "./preVolunteerCount"
import teamList from "./teamList"
import volunteer from "./volunteer"
import volunteerAdd from "./volunteerAdd"
import volunteerList from "./volunteerList"
@ -12,16 +13,17 @@ import volunteerSet from "./volunteerSet"
import volunteerLogin from "./volunteerLogin"
import volunteerForgot from "./volunteerForgot"
import volunteerNotifsSet from "./volunteerNotifsSet"
import preVolunteerAdd from "./preVolunteerAdd"
import preVolunteerCount from "./preVolunteerCount"
import wishAdd from "./wishAdd"
import wishList from "./wishList"
// Use inferred return type for making correctly Redux types
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export default (history: History) => ({
auth,
wishAdd,
wishList,
javGameList,
preVolunteerAdd,
preVolunteerCount,
teamList,
volunteer,
volunteerAdd,
volunteerList,
@ -29,8 +31,8 @@ export default (history: History) => ({
volunteerLogin,
volunteerForgot,
volunteerNotifsSet,
preVolunteerAdd,
preVolunteerCount,
wishAdd,
wishList,
router: connectRouter(history) as any,
// Register more reducers...
})

48
src/store/teamList.ts Normal file
View File

@ -0,0 +1,48 @@
import { PayloadAction, createSlice, createEntityAdapter } from "@reduxjs/toolkit"
import { StateRequest, toastError, elementListFetch } from "./utils"
import { Team, teamListGet } from "../services/teams"
import { AppThunk, AppState } from "."
const teamAdapter = createEntityAdapter<Team>()
export const initialState = teamAdapter.getInitialState({
readyStatus: "idle",
} as StateRequest)
const teamList = createSlice({
name: "teamList",
initialState,
reducers: {
getRequesting: (state) => {
state.readyStatus = "request"
},
getSuccess: (state, { payload }: PayloadAction<Team[]>) => {
state.readyStatus = "success"
teamAdapter.setAll(state, payload)
},
getFailure: (state, { payload }: PayloadAction<string>) => {
state.readyStatus = "failure"
state.error = payload
},
},
})
export default teamList.reducer
export const { getRequesting, getSuccess, getFailure } = teamList.actions
export const fetchTeamList = elementListFetch(
teamListGet,
getRequesting,
getSuccess,
getFailure,
(error: Error) => toastError(`Erreur lors du chargement des équipes: ${error.message}`)
)
const shouldFetchTeamList = (state: AppState) => state.teamList.readyStatus !== "success"
export const fetchTeamListIfNeed = (): AppThunk => (dispatch, getState) => {
if (shouldFetchTeamList(getState())) return dispatch(fetchTeamList())
return null
}