add board to home page

This commit is contained in:
memeriau 2022-03-13 19:49:39 +01:00
parent b93da3e119
commit 850d5e9da6
17 changed files with 253 additions and 162 deletions

View File

@ -1,7 +1,7 @@
import _ from "lodash" import _ from "lodash"
import React, { memo, useCallback, useEffect, useRef, useState } from "react" import React, { memo, useCallback, useEffect, useRef, useState } from "react"
import isNode from "detect-node" import isNode from "detect-node"
import { useDispatch, useSelector } from "react-redux" import { shallowEqual, useDispatch, useSelector } from "react-redux"
import classnames from "classnames" import classnames from "classnames"
import { fetchVolunteerNotifsSet } from "../../store/volunteerNotifsSet" import { fetchVolunteerNotifsSet } from "../../store/volunteerNotifsSet"
import styles from "./styles.module.scss" import styles from "./styles.module.scss"
@ -9,15 +9,22 @@ import { selectUserJwtToken } from "../../store/auth"
import { VolunteerNotifs } from "../../services/volunteers" import { VolunteerNotifs } from "../../services/volunteers"
// import { TeamWishesForm } from ".." // import { TeamWishesForm } from ".."
import { fetchFor as fetchForTeamWishesForm } from "../VolunteerBoard/TeamWishesForm/TeamWishesForm" import { fetchFor as fetchForTeamWishesForm } from "../VolunteerBoard/TeamWishesForm/TeamWishesForm"
import { AppState } from "../../store"
import Block from "../ui/Content/ContentBlock"
interface Props { let prevNotifs: VolunteerNotifs | undefined
// eslint-disable-next-line react/require-default-props
volunteerNotifs?: VolunteerNotifs
}
const Notifications = ({ volunteerNotifs }: Props): JSX.Element | null => { const Notifications = (): JSX.Element | null => {
const dispatch = useDispatch() const dispatch = useDispatch()
const jwtToken = useSelector(selectUserJwtToken) const jwtToken = useSelector(selectUserJwtToken)
const volunteerNotifs = useSelector((state: AppState) => {
const notifs = state.volunteerNotifsSet?.entity
if (notifs) {
prevNotifs = notifs
return notifs
}
return prevNotifs
}, shallowEqual)
const hidden = volunteerNotifs?.hiddenNotifs || [] const hidden = volunteerNotifs?.hiddenNotifs || []
const notifs: JSX.Element[] = [] const notifs: JSX.Element[] = []
@ -36,19 +43,17 @@ const Notifications = ({ volunteerNotifs }: Props): JSX.Element | null => {
if (!_.includes(hidden, 1)) { if (!_.includes(hidden, 1)) {
notifs.push( notifs.push(
<div key="1"> <div key="1">
<div className={styles.notificationsPage}> <div className={styles.notificationsContent}>
<div className={styles.notificationsContent}> <form onSubmit={onSubmit1}>
<form onSubmit={onSubmit1}> Salut {volunteerNotifs?.firstname} !
Salut {volunteerNotifs?.firstname} ! <div className={styles.notifIntro} key="login-intro">
<div className={styles.notifIntro} key="login-intro"> Ici tu seras notifié(e) des nouvelles importantes et des questions pour
Ici tu seras notifié(e) des nouvelles importantes et des questions lesquelles il nous faudrait absolument ta réponse.
pour lesquelles il nous faudrait absolument ta réponse. <div className={styles.formButtons}>
<div className={styles.formButtons}> <button type="submit">Ok, continuer</button>
<button type="submit">Ok, continuer</button>
</div>
</div> </div>
</form> </div>
</div> </form>
</div> </div>
</div> </div>
) )
@ -80,64 +85,62 @@ const Notifications = ({ volunteerNotifs }: Props): JSX.Element | null => {
if (!_.includes(hidden, 2)) { if (!_.includes(hidden, 2)) {
notifs.push( notifs.push(
<div key="2"> <div key="2">
<div className={styles.notificationsPage}> <div className={styles.notificationsContent}>
<div className={styles.notificationsContent}> <div className={styles.formLine} key="line-participation">
<div className={styles.formLine} key="line-participation"> <form onSubmit={onSubmit2}>
<form onSubmit={onSubmit2}> Si les conditions sanitaires te le permettent, souhaites-tu être
Si les conditions sanitaires te le permettent, souhaites-tu être bénévole à PeL 2022 ?<br />
bénévole à PeL 2022 ?<br /> <label>
<label> <input
<input type="radio"
type="radio" value="oui"
value="oui" name="gender"
name="gender" checked={participation === "oui"}
checked={participation === "oui"} onChange={onChangeValue2}
onChange={onChangeValue2} />{" "}
/>{" "} Oui
Oui </label>
</label> <label>
<label> <input
<input type="radio"
type="radio" value="non"
value="non" name="gender"
name="gender" checked={participation === "non"}
checked={participation === "non"} onChange={onChangeValue2}
onChange={onChangeValue2} />{" "}
/>{" "} Non
Non </label>
</label> <label>
<label> <input
<input type="radio"
type="radio" value="peut-etre"
value="peut-etre" name="gender"
name="gender" checked={participation === "peut-etre"}
checked={participation === "peut-etre"} onChange={onChangeValue2}
onChange={onChangeValue2} />{" "}
/>{" "} Je ne sais pas encore
Je ne sais pas encore </label>
</label> {participation === "peut-etre" ? (
{participation === "peut-etre" ? ( <div>
<div> On te le reproposera dans quelques temps.
On te le reproposera dans quelques temps. <br />
<br /> Si tu as besoin d&apos;infos, viens nous en parler sur le
Si tu as besoin d&apos;infos, viens nous en parler sur le serveur Discord ! Pour le rejoindre,{" "}
serveur Discord ! Pour le rejoindre,{" "} <a
<a href="https://discord.com/invite/eXhjKxSBB4"
href="https://discord.com/invite/eXhjKxSBB4" target="_blank"
target="_blank" rel="noreferrer"
rel="noreferrer" >
> clique ici{" "}
clique ici{" "} </a>
</a> .
.
</div>
) : null}
<div className={styles.formButtons}>
<button type="submit">Confirmer</button>
</div> </div>
<div className={styles.message}>{participationMessage}</div> ) : null}
</form> <div className={styles.formButtons}>
</div> <button type="submit">Confirmer</button>
</div>
<div className={styles.message}>{participationMessage}</div>
</form>
</div> </div>
</div> </div>
</div> </div>
@ -155,27 +158,25 @@ const Notifications = ({ volunteerNotifs }: Props): JSX.Element | null => {
if (!_.includes(hidden, 3)) { if (!_.includes(hidden, 3)) {
notifs.push( notifs.push(
<div key="3"> <div key="3">
<div className={styles.notificationsPage}> <div className={styles.notificationsContent}>
<div className={styles.notificationsContent}> <form onSubmit={onSubmit3}>
<form onSubmit={onSubmit3}> <div
<div className={classnames(styles.notifIntro, styles.notifCentered)}
className={classnames(styles.notifIntro, styles.notifCentered)} key="login-intro"
key="login-intro" >
La{" "}
<a
href="https://mailchi.mp/3c75c3b3a20f/gazette_2020_02-8978118"
onClick={onSubmit3}
> >
La{" "} gazette de février
<a </a>{" "}
href="https://mailchi.mp/3c75c3b3a20f/gazette_2020_02-8978118" est disponible !<br />
onClick={onSubmit3} <div className={styles.formButtons}>
> <button type="submit">Ok, masquer</button>
gazette de février
</a>{" "}
est disponible !<br />
<div className={styles.formButtons}>
<button type="submit">Ok, masquer</button>
</div>
</div> </div>
</form> </div>
</div> </form>
</div> </div>
</div> </div>
) )
@ -381,49 +382,47 @@ Tu n'y es absolument pas obligé(e) ! C'est juste plus pratique.
if (notifs.length === 0) { if (notifs.length === 0) {
notifs.push( notifs.push(
<div key="pushNotifs"> <div key="pushNotifs">
<div className={styles.pushNotificationsPage}> <Block title="Notifications">
<div className={styles.pushNotificationsContent}> <div className={styles.formLine} key="line-participation">
<div className={styles.formLine} key="line-participation"> <label>
<label> Tu as fait le tour des dernières infos ou questions importantes, merci !
Tu as fait le tour des dernières infos ou questions importantes, :)
merci ! :) <br />
<br /> <br />
<br /> 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 d&apos;autres spécifiquement pour toi ?<br />
aura d&apos;autres spécifiquement pour toi ?<br />
<span className={styles.sousMessage}>
(Ça nous simplifierait la vie, on a des soucis à contacter les
bénévoles par email.)
</span>
<label>
<input
type="radio"
value="oui"
name="gender"
checked={acceptsNotifs === "oui"}
onChange={onChangePushNotifs}
/>{" "}
Oui
</label>
<label>
<input
type="radio"
value="non"
name="gender"
checked={acceptsNotifs === "non"}
onChange={onChangePushNotifs}
/>{" "}
Non
</label>
</label>
<div className={styles.message}>{notifMessage}</div>
<span className={styles.sousMessage}> <span className={styles.sousMessage}>
Pas besoin de valider, le site mémorise automatiquement si tu (Ça nous simplifierait la vie, on a des soucis à contacter les
changes ta réponse. bénévoles par email.)
</span> </span>
</div> <label>
<input
type="radio"
value="oui"
name="gender"
checked={acceptsNotifs === "oui"}
onChange={onChangePushNotifs}
/>{" "}
Oui
</label>
<label>
<input
type="radio"
value="non"
name="gender"
checked={acceptsNotifs === "non"}
onChange={onChangePushNotifs}
/>{" "}
Non
</label>
</label>
<div className={styles.message}>{notifMessage}</div>
<span className={styles.sousMessage}>
Pas besoin de valider, le site mémorise automatiquement si tu changes ta
réponse.
</span>
</div> </div>
</div> </Block>
</div> </div>
) )
} }

View File

@ -6,7 +6,7 @@
} }
.notificationsContent { .notificationsContent {
@include page-content-wrapper; @include inner-content-wrapper;
} }
.pushNotificationsPage { .pushNotificationsPage {
@ -14,7 +14,7 @@
} }
.pushNotificationsContent { .pushNotificationsContent {
@include page-content-wrapper; @include inner-content-wrapper;
} }
.notifIntro { .notifIntro {

View File

@ -6,16 +6,29 @@ import ParticipationDetailsFormModal from "./ParticipationDetailsForm/Participat
import TeamWishes from "./TeamWishes/TeamWishes" import TeamWishes from "./TeamWishes/TeamWishes"
import TeamWishesFormModal from "./TeamWishesForm/TeamWishesFormModal" import TeamWishesFormModal from "./TeamWishesForm/TeamWishesFormModal"
import withUserConnected from "../../utils/withUserConnected" import withUserConnected from "../../utils/withUserConnected"
import { fetchVolunteerDayWishesSetIfNeed } from "../../store/volunteerDayWishesSet"
import { fetchVolunteerParticipationDetailsSetIfNeed } from "../../store/volunteerParticipationDetailsSet"
import { fetchTeamListIfNeed } from "../../store/teamList"
import { fetchVolunteerTeamWishesSetIfNeed } from "../../store/volunteerTeamWishesSet"
import ContentTitle from "../ui/Content/ContentTitle"
const Board: FC = (): JSX.Element => ( const Board: FC = (): JSX.Element => (
<div> <>
<ContentTitle title="Pour le jour J" />
<DayWishes /> <DayWishes />
<DayWishesFormModal /> <DayWishesFormModal />
<ParticipationDetails /> <ParticipationDetails />
<ParticipationDetailsFormModal /> <ParticipationDetailsFormModal />
<TeamWishes /> <TeamWishes />
<TeamWishesFormModal /> <TeamWishesFormModal />
</div> </>
) )
export default memo(withUserConnected(Board)) export default memo(withUserConnected(Board))
export const fetchFor = [
fetchVolunteerDayWishesSetIfNeed,
fetchVolunteerParticipationDetailsSetIfNeed,
fetchTeamListIfNeed,
fetchVolunteerTeamWishesSetIfNeed,
]

View File

@ -65,7 +65,7 @@ const TeamWishesForm: FC<Props> = ({ afterSubmit }): JSX.Element | null => {
{selection.map((item) => { {selection.map((item) => {
const team = teams.find((t: any) => t.id === item) const team = teams.find((t: any) => t.id === item)
if (!team) return null if (!team) return null
return <li>{team.name}</li> return <li key={team.id}>{team.name}</li>
})} })}
</ol> </ol>
<div className={styles.commentWrapper}> <div className={styles.commentWrapper}>

View File

@ -0,0 +1,8 @@
import { FC, memo } from "react"
import styles from "./styles.module.scss"
const VolunteerConfirmation: FC = (): JSX.Element => (
<div className={styles.root}>&#10003; Tu es bénévole pour le festival de 2022. Merci !</div>
)
export default memo(VolunteerConfirmation)

View File

@ -0,0 +1,7 @@
@import "../../theme/variables";
.root {
margin-bottom: 10px;
text-align: center;
color: $color-green;
}

View File

@ -12,6 +12,7 @@ import VolunteerList from "./VolunteerList"
import VolunteerInfo from "./VolunteerInfo" import VolunteerInfo from "./VolunteerInfo"
import VolunteerSet from "./VolunteerSet" import VolunteerSet from "./VolunteerSet"
import WishAdd from "./WishAdd" import WishAdd from "./WishAdd"
import { fetchFor as fetchForBoardForms } from "./VolunteerBoard/Board"
export { export {
AnnouncementLink, AnnouncementLink,
@ -28,4 +29,5 @@ export {
VolunteerList, VolunteerList,
VolunteerSet, VolunteerSet,
WishAdd, WishAdd,
fetchForBoardForms,
} }

View File

@ -0,0 +1,21 @@
import { FC, memo, ReactNode } from "react"
import styles from "./styles.module.scss"
import ContentTitle from "./ContentTitle"
interface Props {
children: ReactNode
title?: string | null
}
const ContentBlock: FC<Props> = ({ children, title }): JSX.Element => (
<div>
{title && <ContentTitle title={title} />}
<div className={styles.content}>{children}</div>
</div>
)
ContentBlock.defaultProps = {
title: null,
}
export default memo(ContentBlock)

View File

@ -0,0 +1,12 @@
import { FC, memo } from "react"
import styles from "./styles.module.scss"
interface Props {
title: string
}
const ContentTitle: FC<Props> = ({ title }): JSX.Element => (
<h2 className={styles.title}>{title}</h2>
)
export default memo(ContentTitle)

View File

@ -0,0 +1,12 @@
@import "../../../theme/mixins";
.title {
margin: 30px 10px 0;
padding: 0;
font-weight: 600;
font-size: 1.1em;
}
.content {
@include inner-content-wrapper;
}

View File

@ -1,4 +1,4 @@
@import "../../theme/mixins"; @import "../../../theme/mixins";
.pageWrapper { .pageWrapper {
@include page-wrapper-center; @include page-wrapper-center;

View File

@ -4,7 +4,7 @@ import { useSelector } from "react-redux"
import { AppThunk } from "../../store" import { AppThunk } from "../../store"
import { selectUserJwtToken } from "../../store/auth" import { selectUserJwtToken } from "../../store/auth"
import Page from "../../components/Page/Page" import Page from "../../components/ui/Page/Page"
import Board from "../../components/VolunteerBoard/Board" import Board from "../../components/VolunteerBoard/Board"
import { fetchVolunteerDayWishesSetIfNeed } from "../../store/volunteerDayWishesSet" import { fetchVolunteerDayWishesSetIfNeed } from "../../store/volunteerDayWishesSet"
import { fetchVolunteerParticipationDetailsSetIfNeed } from "../../store/volunteerParticipationDetailsSet" import { fetchVolunteerParticipationDetailsSetIfNeed } from "../../store/volunteerParticipationDetailsSet"

View File

@ -1,35 +1,33 @@
import { FC, memo } from "react" import { FC, memo } from "react"
import { RouteComponentProps, Link } from "react-router-dom" import { RouteComponentProps, Link } from "react-router-dom"
import { useSelector, shallowEqual } from "react-redux" import { useSelector } from "react-redux"
import { Helmet } from "react-helmet" import { Helmet } from "react-helmet"
import { AppState, AppThunk } from "../../store" import { AppThunk } from "../../store"
import { LoginForm, Notifications, fetchForTeamWishesForm } from "../../components" import { LoginForm, Notifications, fetchForBoardForms } from "../../components"
import styles from "./styles.module.scss" import styles from "./styles.module.scss"
import { fetchVolunteerNotifsSetIfNeed } from "../../store/volunteerNotifsSet" import { fetchVolunteerNotifsSetIfNeed, hasWaitingNotifs } from "../../store/volunteerNotifsSet"
import { VolunteerNotifs } from "../../services/volunteers"
import { selectUserJwtToken } from "../../store/auth" import { selectUserJwtToken } from "../../store/auth"
import Board from "../../components/VolunteerBoard/Board"
import Page from "../../components/ui/Page/Page"
import VolunteerConfirmation from "../../components/VolunteerConfirmation/VolunteerConfirmation"
export type Props = RouteComponentProps export type Props = RouteComponentProps
let prevNotifs: VolunteerNotifs | undefined
const HomePage: FC<Props> = (): JSX.Element => { const HomePage: FC<Props> = (): JSX.Element => {
const jwtToken = useSelector(selectUserJwtToken) const jwtToken = useSelector(selectUserJwtToken)
const waitingNotifs = useSelector(hasWaitingNotifs)
const volunteerNotifs = useSelector((state: AppState) => {
const notifs = state.volunteerNotifsSet?.entity
if (notifs) {
prevNotifs = notifs
return notifs
}
return prevNotifs
}, shallowEqual)
if (jwtToken === undefined) return <p>Loading...</p> if (jwtToken === undefined) return <p>Loading...</p>
if (jwtToken) { if (jwtToken) {
return <Notifications volunteerNotifs={volunteerNotifs} /> return (
<Page>
{!waitingNotifs && <VolunteerConfirmation />}
<Notifications />
{!waitingNotifs && <Board />}
</Page>
)
} }
return ( return (
<div> <div>
@ -51,7 +49,7 @@ const HomePage: FC<Props> = (): JSX.Element => {
// Fetch server-side data here // Fetch server-side data here
export const loadData = (): AppThunk[] => [ export const loadData = (): AppThunk[] => [
fetchVolunteerNotifsSetIfNeed(), fetchVolunteerNotifsSetIfNeed(),
...fetchForTeamWishesForm.map((f) => f()), ...fetchForBoardForms.map((f) => f()),
] ]
export default memo(HomePage) export default memo(HomePage)

View File

@ -4,7 +4,7 @@ import { useSelector } from "react-redux"
import { AppThunk } from "../../store" import { AppThunk } from "../../store"
import { selectUserJwtToken } from "../../store/auth" import { selectUserJwtToken } from "../../store/auth"
import Page from "../../components/Page/Page" import Page from "../../components/ui/Page/Page"
import { fetchVolunteerListIfNeed } from "../../store/volunteerList" import { fetchVolunteerListIfNeed } from "../../store/volunteerList"
export type Props = RouteComponentProps export type Props = RouteComponentProps

View File

@ -1,4 +1,5 @@
import { PayloadAction, createSlice } from "@reduxjs/toolkit" import { PayloadAction, createSlice, createSelector } from "@reduxjs/toolkit"
import get from "lodash/get"
import { StateRequest, toastError, elementFetch } from "./utils" import { StateRequest, toastError, elementFetch } from "./utils"
import { VolunteerNotifs } from "../services/volunteers" import { VolunteerNotifs } from "../services/volunteers"
@ -57,3 +58,21 @@ export const fetchVolunteerNotifsSetIfNeed =
return null return null
} }
export const openedNotifsIds = [1, 2, 3]
export const selectVolunteerNotifsSetState = (state: AppState): StateVolunteerNotifsSet =>
state.volunteerNotifsSet
export const selectHiddenNotifs = createSelector(selectVolunteerNotifsSetState, (notifState) =>
get(notifState, "entity.hiddenNotifs", [])
)
export const selectWaitingsNotifs = createSelector(selectHiddenNotifs, (hidden) =>
openedNotifsIds.filter((id) => !hidden.find((hiddenId: number) => hiddenId === id))
)
export const hasWaitingNotifs = createSelector(
selectWaitingsNotifs,
(waiting) => waiting.length > 0
)

View File

@ -37,7 +37,7 @@
} }
@mixin inner-content-wrapper() { @mixin inner-content-wrapper() {
margin: 10px 0; margin: 5px 0 10px;
padding: 10px; padding: 10px;
border-radius: 5px; border-radius: 5px;
background-color: $color-grey-lighter; background-color: $color-grey-lighter;