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

View File

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

View File

@ -6,16 +6,29 @@ import ParticipationDetailsFormModal from "./ParticipationDetailsForm/Participat
import TeamWishes from "./TeamWishes/TeamWishes"
import TeamWishesFormModal from "./TeamWishesForm/TeamWishesFormModal"
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 => (
<div>
<>
<ContentTitle title="Pour le jour J" />
<DayWishes />
<DayWishesFormModal />
<ParticipationDetails />
<ParticipationDetailsFormModal />
<TeamWishes />
<TeamWishesFormModal />
</div>
</>
)
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) => {
const team = teams.find((t: any) => t.id === item)
if (!team) return null
return <li>{team.name}</li>
return <li key={team.id}>{team.name}</li>
})}
</ol>
<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 VolunteerSet from "./VolunteerSet"
import WishAdd from "./WishAdd"
import { fetchFor as fetchForBoardForms } from "./VolunteerBoard/Board"
export {
AnnouncementLink,
@ -28,4 +29,5 @@ export {
VolunteerList,
VolunteerSet,
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 {
@include page-wrapper-center;

View File

@ -4,7 +4,7 @@ import { useSelector } from "react-redux"
import { AppThunk } from "../../store"
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 { fetchVolunteerDayWishesSetIfNeed } from "../../store/volunteerDayWishesSet"
import { fetchVolunteerParticipationDetailsSetIfNeed } from "../../store/volunteerParticipationDetailsSet"

View File

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

View File

@ -4,7 +4,7 @@ import { useSelector } from "react-redux"
import { AppThunk } from "../../store"
import { selectUserJwtToken } from "../../store/auth"
import Page from "../../components/Page/Page"
import Page from "../../components/ui/Page/Page"
import { fetchVolunteerListIfNeed } from "../../store/volunteerList"
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 { VolunteerNotifs } from "../services/volunteers"
@ -57,3 +58,21 @@ export const fetchVolunteerNotifsSetIfNeed =
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() {
margin: 10px 0;
margin: 5px 0 10px;
padding: 10px;
border-radius: 5px;
background-color: $color-grey-lighter;