mirror of
https://github.com/Paris-est-Ludique/intranet.git
synced 2025-06-08 08:34:20 +02:00
commit
f0ecf4acda
60
src/components/Knowledge/KnowledgeStats.tsx
Normal file
60
src/components/Knowledge/KnowledgeStats.tsx
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import React, { memo } from "react"
|
||||||
|
import { useSelector } from "react-redux"
|
||||||
|
import styles from "./styles.module.scss"
|
||||||
|
import { fetchBoxListIfNeed, selectSortedUniqueDetailedBoxes } from "../../store/boxList"
|
||||||
|
import {
|
||||||
|
fetchVolunteerDetailedKnowledgeListIfNeed,
|
||||||
|
selectVolunteerDetailedKnowledgeList,
|
||||||
|
} from "../../store/volunteerDetailedKnowledgeList"
|
||||||
|
import { DetailedBox } from "../../services/boxes"
|
||||||
|
import { VolunteerDetailedKnowledge } from "../../services/volunteers"
|
||||||
|
|
||||||
|
const KnowledgeStats: React.FC = (): JSX.Element | null => {
|
||||||
|
const detailedBoxes = useSelector(selectSortedUniqueDetailedBoxes) as DetailedBox[]
|
||||||
|
const volunteerDetailedKnowledgeList = useSelector(selectVolunteerDetailedKnowledgeList)
|
||||||
|
|
||||||
|
const knowledgeStats = detailedBoxes.map((box) => ({
|
||||||
|
ok: wiseVolunteersNumber(volunteerDetailedKnowledgeList, box.gameId, "ok"),
|
||||||
|
bof: wiseVolunteersNumber(volunteerDetailedKnowledgeList, box.gameId, "bof"),
|
||||||
|
box,
|
||||||
|
}))
|
||||||
|
|
||||||
|
const unknownGames = knowledgeStats.filter((stat) => stat.ok === 0 && stat.bof === 0)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p>{unknownGames.length} jeux non connus par des bénévoles :</p>
|
||||||
|
<ul>
|
||||||
|
{knowledgeStats
|
||||||
|
.filter((stat) => stat.ok === 0 && stat.bof === 0)
|
||||||
|
.map((stat) => (
|
||||||
|
<li key={stat.box.id}>
|
||||||
|
<a
|
||||||
|
href={`https://boardgamegeek.com/boardgame/${stat.box.bggId}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className={styles.title}
|
||||||
|
>
|
||||||
|
{stat.box.title}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function wiseVolunteersNumber(
|
||||||
|
volunteersKnowledge: VolunteerDetailedKnowledge[],
|
||||||
|
gameId: number,
|
||||||
|
wiseness: "ok" | "bof"
|
||||||
|
): number {
|
||||||
|
return volunteersKnowledge.filter(
|
||||||
|
(v) =>
|
||||||
|
v[wiseness].includes(gameId) && (v.dayWishes.includes("S") || v.dayWishes.includes("D"))
|
||||||
|
).length
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(KnowledgeStats)
|
||||||
|
|
||||||
|
export const fetchFor = [fetchBoxListIfNeed, fetchVolunteerDetailedKnowledgeListIfNeed]
|
@ -1,9 +1,11 @@
|
|||||||
import { FC, useCallback, useState } from "react"
|
import { FC, useCallback, useState, useEffect, useMemo } from "react"
|
||||||
import { useSelector } from "react-redux"
|
import { useSelector } from "react-redux"
|
||||||
import classnames from "classnames"
|
import classnames from "classnames"
|
||||||
import { isUserConnected, routerSelector, selectUserRoles } from "../../store/auth"
|
import { isUserConnected, routerSelector, selectUserRoles, selectUserId } from "../../store/auth"
|
||||||
import styles from "./styles.module.scss"
|
import styles from "./styles.module.scss"
|
||||||
import ROLES from "../../utils/roles.constants"
|
import ROLES from "../../utils/roles.constants"
|
||||||
|
import useAction from "../../utils/useAction"
|
||||||
|
import { fetchVolunteerListIfNeed, selectVolunteerList } from "../../store/volunteerList"
|
||||||
|
|
||||||
interface MenuItemProps {
|
interface MenuItemProps {
|
||||||
name: string
|
name: string
|
||||||
@ -29,6 +31,27 @@ const RestrictMenuItem: FC<RestrictMenuItemProps> = ({ name, pathname, role }):
|
|||||||
return roles.includes(role) ? <MenuItem name={name} pathname={pathname} /> : <div />
|
return roles.includes(role) ? <MenuItem name={name} pathname={pathname} /> : <div />
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TeamMenuItemProps extends MenuItemProps {
|
||||||
|
team: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const TeamMenuItem: FC<TeamMenuItemProps> = ({ name, pathname, team }): JSX.Element => {
|
||||||
|
const fetch = useAction(fetchVolunteerListIfNeed)
|
||||||
|
const userId = useSelector(selectUserId)
|
||||||
|
useEffect(() => {
|
||||||
|
if (userId) fetch()
|
||||||
|
}, [userId, fetch])
|
||||||
|
const volunteers = useSelector(selectVolunteerList)
|
||||||
|
const user = useMemo(
|
||||||
|
() => volunteers.find((volunteer) => volunteer.id === userId),
|
||||||
|
[volunteers, userId]
|
||||||
|
)
|
||||||
|
return user?.team === team ? <MenuItem name={name} pathname={pathname} /> : <div />
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hardcoded value of the "Jeux à volonté" team
|
||||||
|
const TEAM_JAV = 2
|
||||||
|
|
||||||
const MainMenu: FC = (): JSX.Element => {
|
const MainMenu: FC = (): JSX.Element => {
|
||||||
const connected = useSelector(isUserConnected)
|
const connected = useSelector(isUserConnected)
|
||||||
const [opened, setOpened] = useState(false)
|
const [opened, setOpened] = useState(false)
|
||||||
@ -54,7 +77,8 @@ const MainMenu: FC = (): JSX.Element => {
|
|||||||
<MenuItem name="Mon profil" pathname="/profil" />
|
<MenuItem name="Mon profil" pathname="/profil" />
|
||||||
{/* <MenuItem name="Emprunter" pathname="/emprunter" />
|
{/* <MenuItem name="Emprunter" pathname="/emprunter" />
|
||||||
<MenuItem name="Emprunts" pathname="/emprunts" /> */}
|
<MenuItem name="Emprunts" pathname="/emprunts" /> */}
|
||||||
{/* <MenuItem name="Mes connaissances" pathname="/connaissances" /> */}
|
<TeamMenuItem team={TEAM_JAV} name="Mes connaissances" pathname="/connaissances" />
|
||||||
|
<TeamMenuItem team={TEAM_JAV} name="Stats" pathname="/connaissancesStats" />
|
||||||
<RestrictMenuItem
|
<RestrictMenuItem
|
||||||
role={ROLES.ASSIGNER}
|
role={ROLES.ASSIGNER}
|
||||||
name="Gestion équipes"
|
name="Gestion équipes"
|
||||||
|
@ -16,6 +16,7 @@ import LoginForm from "./LoginForm"
|
|||||||
import KnowledgeBoxList, { fetchFor as fetchForKnowledge } from "./Knowledge/KnowledgeBoxList"
|
import KnowledgeBoxList, { fetchFor as fetchForKnowledge } from "./Knowledge/KnowledgeBoxList"
|
||||||
import KnowledgeCard, { fetchFor as fetchForKnowledgeCard } from "./Knowledge/KnowledgeCard"
|
import KnowledgeCard, { fetchFor as fetchForKnowledgeCard } from "./Knowledge/KnowledgeCard"
|
||||||
import KnowledgeIntro from "./Knowledge/KnowledgeIntro"
|
import KnowledgeIntro from "./Knowledge/KnowledgeIntro"
|
||||||
|
import KnowledgeStats from "./Knowledge/KnowledgeStats"
|
||||||
import Asks, { fetchFor as fetchForAsks } from "./Asks"
|
import Asks, { fetchFor as fetchForAsks } from "./Asks"
|
||||||
import ParticipationDetailsForm, {
|
import ParticipationDetailsForm, {
|
||||||
fetchFor as fetchForParticipationDetailsForm,
|
fetchFor as fetchForParticipationDetailsForm,
|
||||||
@ -46,6 +47,7 @@ export {
|
|||||||
KnowledgeBoxList,
|
KnowledgeBoxList,
|
||||||
fetchForKnowledge,
|
fetchForKnowledge,
|
||||||
KnowledgeIntro,
|
KnowledgeIntro,
|
||||||
|
KnowledgeStats,
|
||||||
Loading,
|
Loading,
|
||||||
LoaningIntro,
|
LoaningIntro,
|
||||||
Loaning,
|
Loaning,
|
||||||
|
32
src/pages/KnowledgeStats/KnowledgeStatsPage.tsx
Normal file
32
src/pages/KnowledgeStats/KnowledgeStatsPage.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { FC, memo } from "react"
|
||||||
|
import { useSelector } from "react-redux"
|
||||||
|
import { RouteComponentProps } from "react-router-dom"
|
||||||
|
import { Helmet } from "react-helmet"
|
||||||
|
|
||||||
|
import { AppThunk } from "../../store"
|
||||||
|
import styles from "../Knowledge/styles.module.scss"
|
||||||
|
import { KnowledgeStats, fetchForKnowledgeCard, LoginForm } from "../../components"
|
||||||
|
import { selectUserJwtToken } from "../../store/auth"
|
||||||
|
|
||||||
|
export type Props = RouteComponentProps
|
||||||
|
|
||||||
|
const KnowledgeStatsPage: FC<Props> = (): JSX.Element => {
|
||||||
|
const jwtToken = useSelector(selectUserJwtToken)
|
||||||
|
if (jwtToken === undefined) return <p>Loading...</p>
|
||||||
|
if (!jwtToken) {
|
||||||
|
return <LoginForm loginNeeded />
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className={styles.knowledgesPage}>
|
||||||
|
<div className={styles.knowledgesContent}>
|
||||||
|
<Helmet title="Stats des jeux connus" />
|
||||||
|
<KnowledgeStats />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch server-side data here
|
||||||
|
export const loadData = (): AppThunk[] => [...fetchForKnowledgeCard.map((f) => f())]
|
||||||
|
|
||||||
|
export default memo(KnowledgeStatsPage)
|
16
src/pages/KnowledgeStats/index.tsx
Normal file
16
src/pages/KnowledgeStats/index.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import loadable from "@loadable/component"
|
||||||
|
|
||||||
|
import { Loading, ErrorBoundary } from "../../components"
|
||||||
|
import { Props, loadData } from "./KnowledgeStatsPage"
|
||||||
|
|
||||||
|
const Knowledges = loadable(() => import("./KnowledgeStatsPage"), {
|
||||||
|
fallback: <Loading />,
|
||||||
|
})
|
||||||
|
|
||||||
|
export default (props: Props): JSX.Element => (
|
||||||
|
<ErrorBoundary>
|
||||||
|
<Knowledges {...props} />
|
||||||
|
</ErrorBoundary>
|
||||||
|
)
|
||||||
|
|
||||||
|
export { loadData }
|
@ -10,6 +10,7 @@ import AsyncAnnouncements, { loadData as loadAnnouncementsData } from "../pages/
|
|||||||
import AsyncTeamAssignment, { loadData as loadTeamAssignmentData } from "../pages/TeamAssignment"
|
import AsyncTeamAssignment, { loadData as loadTeamAssignmentData } from "../pages/TeamAssignment"
|
||||||
import AsyncRegisterPage, { loadData as loadRegisterPage } from "../pages/Register"
|
import AsyncRegisterPage, { loadData as loadRegisterPage } from "../pages/Register"
|
||||||
import AsyncKnowledge, { loadData as loadKnowledgeData } from "../pages/Knowledge"
|
import AsyncKnowledge, { loadData as loadKnowledgeData } from "../pages/Knowledge"
|
||||||
|
import AsyncKnowledgeStats, { loadData as loadKnowledgeStatsData } from "../pages/KnowledgeStats"
|
||||||
// import AsyncLoans, { loadData as loadLoansData } from "../pages/Loans"
|
// import AsyncLoans, { loadData as loadLoansData } from "../pages/Loans"
|
||||||
import AsyncLoaning, { loadData as loadLoaningData } from "../pages/Loaning"
|
import AsyncLoaning, { loadData as loadLoaningData } from "../pages/Loaning"
|
||||||
import AsyncKnowledgeCards, { loadData as loadCardKnowledgeData } from "../pages/KnowledgeCards"
|
import AsyncKnowledgeCards, { loadData as loadCardKnowledgeData } from "../pages/KnowledgeCards"
|
||||||
@ -46,6 +47,11 @@ export default [
|
|||||||
component: AsyncKnowledge,
|
component: AsyncKnowledge,
|
||||||
loadData: loadKnowledgeData,
|
loadData: loadKnowledgeData,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/connaissancesStats",
|
||||||
|
component: AsyncKnowledgeStats,
|
||||||
|
loadData: loadKnowledgeStatsData,
|
||||||
|
},
|
||||||
// {
|
// {
|
||||||
// path: "/emprunts",
|
// path: "/emprunts",
|
||||||
// component: AsyncLoans,
|
// component: AsyncLoans,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user