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,13 +43,12 @@ 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 Ici tu seras notifié(e) des nouvelles importantes et des questions pour
pour lesquelles il nous faudrait absolument ta réponse. 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>
@ -50,7 +56,6 @@ const Notifications = ({ volunteerNotifs }: Props): JSX.Element | null => {
</form> </form>
</div> </div>
</div> </div>
</div>
) )
} }
@ -80,7 +85,6 @@ 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}>
@ -140,7 +144,6 @@ const Notifications = ({ volunteerNotifs }: Props): JSX.Element | null => {
</div> </div>
</div> </div>
</div> </div>
</div>
) )
} }
@ -155,7 +158,6 @@ 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
@ -177,7 +179,6 @@ const Notifications = ({ volunteerNotifs }: Props): JSX.Element | null => {
</form> </form>
</div> </div>
</div> </div>
</div>
) )
} }
@ -381,16 +382,15 @@ 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, Tu as fait le tour des dernières infos ou questions importantes, merci !
merci ! :) :)
<br /> <br />
<br /> <br />
Acceptes-tu de recevoir une alerte dans ton navigateur quand on en Acceptes-tu de recevoir une alerte dans ton navigateur quand on en aura
aura d&apos;autres spécifiquement pour toi ?<br /> d&apos;autres spécifiquement pour toi ?<br />
<span className={styles.sousMessage}> <span className={styles.sousMessage}>
(Ça nous simplifierait la vie, on a des soucis à contacter les (Ça nous simplifierait la vie, on a des soucis à contacter les
bénévoles par email.) bénévoles par email.)
@ -418,12 +418,11 @@ Tu n'y es absolument pas obligé(e) ! C'est juste plus pratique.
</label> </label>
<div className={styles.message}>{notifMessage}</div> <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 Pas besoin de valider, le site mémorise automatiquement si tu changes ta
changes ta réponse. réponse.
</span> </span>
</div> </div>
</div> </Block>
</div>
</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;