Add /profil to notifications, improve responsiveness

This commit is contained in:
pikiou 2022-03-25 01:37:11 +01:00
parent f65963d91c
commit 8fb7b5287b
21 changed files with 538 additions and 280 deletions

View File

@ -0,0 +1,53 @@
import { FC, ReactNode, useState } from "react"
import styles from "./styles.module.scss"
type Props = {
children?: ReactNode | undefined
text: string
onClick?: () => void
}
const FormButton: FC<Props> = ({ children, text, onClick }): JSX.Element => {
const [doShowMessage, setDoShowMessage] = useState<boolean>(false)
const showMessage = () => {
setDoShowMessage(true)
}
const onIgnore = () => {
setDoShowMessage(true)
onClick?.()
}
const onCloseIgnore = () => {
setDoShowMessage(false)
}
return (
<>
<button
type="button"
className={styles.greyButton}
onClick={!children ? onClick : showMessage}
>
{text}
</button>
{doShowMessage && (
<div className={styles.infoBeforeIgnore}>
<button type="button" className={styles.closeButton} onClick={onCloseIgnore}>
&#10005;
</button>
<div>{children}</div>
<button type="button" className={styles.greyButton} onClick={onIgnore}>
Ok, ignorer
</button>
</div>
)}
</>
)
}
FormButton.defaultProps = {
children: undefined,
onClick: undefined,
}
export default FormButton

View File

@ -0,0 +1,30 @@
@import "../../../theme/mixins";
.greyButton {
@include form-grey-button;
margin-top: 10px;
}
.infoBeforeIgnore {
margin-top: 15px;
position: relative;
padding: 15px;
outline: 0;
background-color: $color-white;
}
.closeButton {
position: absolute;
padding: 0;
z-index: 10;
top: 10px;
right: 10px;
width: 20px;
height: 20px;
border-radius: 0;
line-height: 20px;
color: $color-grey-dark;
text-align: center;
background: none;
cursor: pointer;
}

View File

@ -6,6 +6,7 @@ import styles from "./styles.module.scss"
const MainMenu: FC = (): JSX.Element | null => {
const connected = useSelector(isUserConnected)
const [opened, setOpened] = useState(false)
const onOpen = useCallback(() => {
@ -24,6 +25,9 @@ const MainMenu: FC = (): JSX.Element | null => {
</button>
<ul className={classnames(styles.mainMenu, opened && styles.opened)}>
<li className={styles.mainMenuItem}>
<a href="/">Page d'accueil</a>
</li>
<li className={styles.mainMenuItem}>
<a href="/annonces">Annonces</a>
</li>

View File

@ -57,6 +57,9 @@
text-align: center;
font-size: 22px;
color: $color-black;
background-color: $color-white;
border-radius: 4px;
box-shadow: 1px 1px 2px $color-black;
}
}

View File

@ -1,16 +1,25 @@
import _ from "lodash"
import _, { get } from "lodash"
import React, { memo, useCallback, useEffect, useRef, useState } from "react"
import isNode from "detect-node"
import { shallowEqual, useDispatch, useSelector } from "react-redux"
import classnames from "classnames"
import { fetchVolunteerNotifsSet } from "../../store/volunteerNotifsSet"
import styles from "./styles.module.scss"
import { selectUserJwtToken } from "../../store/auth"
import { VolunteerNotifs } from "../../services/volunteers"
// import { TeamWishesForm } from ".."
import { fetchFor as fetchForTeamWishesForm } from "../VolunteerBoard/TeamWishesForm/TeamWishesForm"
import TeamWishesForm, {
fetchFor as fetchForTeamWishesForm,
} from "../VolunteerBoard/TeamWishesForm/TeamWishesForm"
import { AppState } from "../../store"
import Block from "../ui/Content/ContentBlock"
import { useUserTeamWishes } from "../VolunteerBoard/teamWishes.utils"
import { useUserDayWishes } from "../VolunteerBoard/daysWishes.utils"
import ParticipationDetailsForm, {
fetchFor as fetchForarticipationDetailsForm,
} from "../VolunteerBoard/ParticipationDetailsForm/ParticipationDetailsForm"
import DayWishesForm, {
fetchFor as fetchForDayWishesForm,
} from "../VolunteerBoard/DayWishesForm/DayWishesForm"
import { useUserParticipationDetails } from "../VolunteerBoard/participationDetails.utils"
import FormButton from "../Form/FormButton/FormButton"
let prevNotifs: VolunteerNotifs | undefined
@ -28,118 +37,28 @@ const Notifications = (): JSX.Element | null => {
const hidden = volunteerNotifs?.hiddenNotifs || []
const notifs: JSX.Element[] = []
const onSubmit1 = useCallback(
(event: React.SyntheticEvent): void => {
event.preventDefault()
dispatch(
fetchVolunteerNotifsSet(jwtToken, 0, {
hiddenNotifs: [...(volunteerNotifs?.hiddenNotifs || []), 1],
})
)
},
[dispatch, jwtToken, volunteerNotifs]
)
const onSubmit1 = useCallback((): void => {
dispatch(
fetchVolunteerNotifsSet(jwtToken, 0, {
hiddenNotifs: [...(volunteerNotifs?.hiddenNotifs || []), 1],
})
)
}, [dispatch, jwtToken, volunteerNotifs])
if (!_.includes(hidden, 1)) {
notifs.push(
<div key="1">
<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>
</form>
</div>
</div>
)
}
const [participation, setParticipation] = useState(volunteerNotifs?.active || "inconnu")
const [participationMessage, setParticipationMessage] = useState("")
const onChangeValue2 = (e: React.ChangeEvent<HTMLInputElement>) =>
setParticipation(e.target.value)
const onSubmit2 = useCallback(
(event: React.SyntheticEvent): void => {
event.preventDefault()
if (participation === "inconnu") {
setParticipationMessage("Il nous faudrait une réponse ^^")
return
}
dispatch(
fetchVolunteerNotifsSet(jwtToken, 0, {
hiddenNotifs: [...(volunteerNotifs?.hiddenNotifs || []), 2],
active: participation,
})
)
},
[dispatch, jwtToken, volunteerNotifs, participation]
)
if (!_.includes(hidden, 2)) {
notifs.push(
<div key="2">
<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="participation"
checked={participation === "oui"}
onChange={onChangeValue2}
/>{" "}
Oui
</label>
<label>
<input
type="radio"
value="non"
name="participation"
checked={participation === "non"}
onChange={onChangeValue2}
/>{" "}
Non
</label>
<label>
<input
type="radio"
value="peut-etre"
name="participation"
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 className={styles.notificationsPage}>
<div className={styles.notificationsContentNarrow}>
<form>
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}>
<FormButton onClick={onSubmit1}>Ok, continuer</FormButton>
</div>
) : null}
<div className={styles.formButtons}>
<button type="submit">Confirmer</button>
</div>
<div className={styles.message}>{participationMessage}</div>
</form>
</div>
</div>
@ -147,40 +66,94 @@ const Notifications = (): JSX.Element | null => {
)
}
const onSubmit3 = useCallback((): void => {
dispatch(
fetchVolunteerNotifsSet(jwtToken, 0, {
hiddenNotifs: [...(volunteerNotifs?.hiddenNotifs || []), 3],
})
)
}, [dispatch, jwtToken, volunteerNotifs])
// const [participation, setParticipation] = useState(volunteerNotifs?.active || "inconnu")
// const [participationMessage, setParticipationMessage] = useState("")
// const onChangeValue2 = (e: React.ChangeEvent<HTMLInputElement>) =>
// setParticipation(e.target.value)
if (!_.includes(hidden, 3)) {
notifs.push(
<div key="3">
<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}
>
gazette de février
</a>{" "}
est disponible !<br />
<div className={styles.formButtons}>
<button type="submit">Ok, masquer</button>
</div>
</div>
</form>
</div>
</div>
)
}
// const onSubmit2 = useCallback(
// (): void => {
// if (participation === "inconnu") {
// setParticipationMessage("Il nous faudrait une réponse ^^")
// return
// }
// dispatch(
// fetchVolunteerNotifsSet(jwtToken, 0, {
// hiddenNotifs: [...(volunteerNotifs?.hiddenNotifs || []), 2],
// active: participation,
// })
// )
// },
// [dispatch, jwtToken, volunteerNotifs, participation]
// )
// if (!_.includes(hidden, 2)) {
// notifs.push(
// <div key="2">
// <div className={styles.notificationsPage}>
// <div className={styles.notificationsContentNarrow}>
// <div className={styles.formLine} key="line-participation">
// <form>
// Si les conditions sanitaires te le permettent, souhaites-tu être
// bénévole à PeL 2022 ?<br />
// <label>
// <input
// type="radio"
// value="oui"
// name="participation"
// checked={participation === "oui"}
// onChange={onChangeValue2}
// />{" "}
// Oui
// </label>
// <label>
// <input
// type="radio"
// value="non"
// name="participation"
// checked={participation === "non"}
// onChange={onChangeValue2}
// />{" "}
// Non
// </label>
// <label>
// <input
// type="radio"
// value="peut-etre"
// name="participation"
// 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}>
// <FormButton onClick={onSubmit2}>Confirmer</FormButton>
// </div>
// <div className={styles.message}>{participationMessage}</div>
// </form>
// </div>
// </div>
// </div>
// </div>
// )
// }
// const onSubmit3 = useCallback((): void => {
// dispatch(
@ -192,16 +165,121 @@ const Notifications = (): JSX.Element | null => {
// if (!_.includes(hidden, 3)) {
// notifs.push(
// <div key="4">
// <div key="3">
// <div className={styles.notificationsPage}>
// <div className={styles.notificationsContent}>
// <TeamWishesForm afterSubmit={onSubmit3} />
// <div className={styles.notificationsContentNarrow}>
// <form>
// <div
// className={classnames(styles.notifIntro, styles.notifCentered)}
// key="login-intro"
// >
// 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}>
// <FormButton onClick={onSubmit3}>Ok, masquer</FormButton>
// </div>
// </div>
// </form>
// </div>
// </div>
// </div>
// )
// }
const answerLaterOnProfile = (
<>
Tu pourras y répondre plus tard sur la page <a href="/profil">Mon profil</a>.
</>
)
const onSubmit10 = useCallback((): void => {
dispatch(
fetchVolunteerNotifsSet(jwtToken, 0, {
hiddenNotifs: [...(volunteerNotifs?.hiddenNotifs || []), 10],
})
)
}, [dispatch, jwtToken, volunteerNotifs])
const [userWishes] = useUserDayWishes()
const participation10 = get(userWishes, "active", "inconnu")
const newSelection = get(userWishes, "dayWishes", [])
const comment10 = get(userWishes, "dayWishesComment", "")
const needToShow10 = participation10 === "inconnu" || (newSelection.length === 0 && !comment10)
if (!_.includes(hidden, 10) && needToShow10) {
notifs.push(
<div key="10">
<div className={styles.notificationsPage}>
<div className={styles.notificationsContent}>
<DayWishesForm afterSubmit={onSubmit10}>
{answerLaterOnProfile}
</DayWishesForm>
</div>
</div>
</div>
)
}
const onSubmit11 = useCallback((): void => {
dispatch(
fetchVolunteerNotifsSet(jwtToken, 0, {
hiddenNotifs: [...(volunteerNotifs?.hiddenNotifs || []), 11],
})
)
}, [dispatch, jwtToken, volunteerNotifs])
const [teamWishesData] = useUserTeamWishes()
const teamWishesString = get(teamWishesData, "teamWishes", [])
const comment = get(teamWishesData, "teamWishesComment", "")
const needToShow11 = teamWishesString.length === 0 && !comment
if (!_.includes(hidden, 11) && needToShow11) {
notifs.push(
<div key="11">
<div className={styles.notificationsPage}>
<div className={styles.notificationsContent}>
<TeamWishesForm afterSubmit={onSubmit11}>
{answerLaterOnProfile}
</TeamWishesForm>
</div>
</div>
</div>
)
}
const onSubmit12 = useCallback((): void => {
dispatch(
fetchVolunteerNotifsSet(jwtToken, 0, {
hiddenNotifs: [...(volunteerNotifs?.hiddenNotifs || []), 12],
})
)
}, [dispatch, jwtToken, volunteerNotifs])
const [participationDetails] = useUserParticipationDetails()
const tshirtSize = get(participationDetails, "tshirtSize", "")
const food = get(participationDetails, "food", "")
const needToShow12 = !tshirtSize && !food
if (!_.includes(hidden, 12) && needToShow12) {
notifs.push(
<div key="12">
<div className={styles.notificationsPage}>
<div className={styles.notificationsContent}>
<ParticipationDetailsForm afterSubmit={onSubmit12}>
{answerLaterOnProfile}
</ParticipationDetailsForm>
</div>
</div>
</div>
)
}
/* DISCORD
Discord nous donne à tous la parole via nos téléphone ou navigateurs, pour organiser le meilleur des festivals !
Il permet de discuter sujet par sujet entre tous les bénévoles, entre les membres d'une même équipe, ou avec ton référent.
@ -382,47 +460,49 @@ Tu n'y es absolument pas obligé(e) ! C'est juste plus pratique.
if (notifs.length === 0) {
notifs.push(
<div key="pushNotifs">
<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 />
<div className={styles.notificationsPage}>
<div className={styles.notificationsContent}>
<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 à t'afficher ici ?<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}>
(Ça nous simplifierait la vie, on a des soucis à contacter les
bénévoles par email.)
Pas besoin de valider, le site mémorise automatiquement si tu
changes ta réponse.
</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}>
Pas besoin de valider, le site mémorise automatiquement si tu changes ta
réponse.
</span>
</div>
</div>
</Block>
</div>
</div>
)
}
@ -437,4 +517,8 @@ Tu n'y es absolument pas obligé(e) ! C'est juste plus pratique.
export default memo(Notifications)
// Fetch server-side data here
export const fetchFor = [...fetchForTeamWishesForm]
export const fetchFor = [
...fetchForTeamWishesForm,
...fetchForarticipationDetailsForm,
...fetchForDayWishesForm,
]

View File

@ -6,7 +6,11 @@
}
.notificationsContent {
@include inner-content-wrapper;
@include page-content-wrapper;
}
.notificationsContentNarrow {
@include page-content-wrapper(520px);
}
.pushNotificationsPage {
@ -14,7 +18,7 @@
}
.pushNotificationsContent {
@include inner-content-wrapper;
@include page-content-wrapper;
}
.notifIntro {

View File

@ -6,11 +6,10 @@ 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"
import { fetchFor as fetchForDayWishesForm } from "./DayWishesForm/DayWishesForm"
import { fetchFor as fetchForParticipationDetailsForm } from "./ParticipationDetailsForm/ParticipationDetailsForm"
import { fetchFor as fetchForTeamWishesForm } from "./TeamWishesForm/TeamWishesForm"
const Board: FC = (): JSX.Element => (
<>
@ -27,8 +26,7 @@ const Board: FC = (): JSX.Element => (
export default memo(withUserConnected(Board))
export const fetchFor = [
fetchVolunteerDayWishesSetIfNeed,
fetchVolunteerParticipationDetailsSetIfNeed,
fetchTeamListIfNeed,
fetchVolunteerTeamWishesSetIfNeed,
...fetchForDayWishesForm,
...fetchForParticipationDetailsForm,
...fetchForTeamWishesForm,
]

View File

@ -32,16 +32,17 @@ const DayWishes: FC = (): JSX.Element | null => {
</div>
)}
{participation === "inconnu" && (
<div className={styles.lineEmpty}>Participation à PeL 2022 non renseignée</div>
<div className={styles.lineEmpty}>
Participation à PeL 2022{" "}
<span className={styles.lineEmpty}>non renseignées</span>
</div>
)}
{participation !== "non" && (
<div className={styles.daysLine}>
<span className={styles.dayLineTitle}>Mes jours :</span>
{dayWishesString && <b>{dayWishesString}</b>}
{!dayWishesString && (
<span className={styles.dayLineEmpty}>Non renseignés</span>
)}
{!dayWishesString && <span className={styles.lineEmpty}>Non renseignés</span>}
</div>
)}
{comment && (

View File

@ -36,7 +36,6 @@
}
.lineEmpty {
margin: 0 5px 5px 0;
color: $color-red;
font-style: italic;
}

View File

@ -1,4 +1,4 @@
import { FC, memo, useCallback, useEffect, useRef, useState } from "react"
import { FC, memo, ReactNode, useCallback, useEffect, useRef, useState } from "react"
import classnames from "classnames"
import get from "lodash/get"
import set from "lodash/set"
@ -10,12 +10,15 @@ import {
useUserDayWishes,
} from "../daysWishes.utils"
import FormButton from "../../Form/FormButton/FormButton"
import { fetchVolunteerDayWishesSetIfNeed } from "../../../store/volunteerDayWishesSet"
import IgnoreButton from "../../Form/IgnoreButton/IgnoreButton"
type Props = {
children?: ReactNode | undefined
afterSubmit?: () => void | undefined
}
const DayWishesForm: FC<Props> = ({ afterSubmit }): JSX.Element => {
const DayWishesForm: FC<Props> = ({ children, afterSubmit }): JSX.Element => {
const [participationState, setParticipation] = useState("inconnu")
const [selection, setSelection] = useState(daysChoiceSelectionDefaultState)
const commentRef = useRef<HTMLTextAreaElement | null>(null)
@ -145,18 +148,35 @@ const DayWishesForm: FC<Props> = ({ afterSubmit }): JSX.Element => {
<label htmlFor="day-choice-comment">Un commentaire, une précision ?</label>
<textarea id="day-choice-comment" ref={commentRef} />
</div>
<div className={styles.dayWishesButtonWrapper}>
<FormButton onClick={onChoiceSubmit}>Enregistrer</FormButton>{" "}
<FormButton onClick={afterSubmit} type="grey">
Annuler
</FormButton>
<div className={styles.buttonWrapper}>
<FormButton onClick={onChoiceSubmit}>Enregistrer</FormButton>
{children === undefined && (
<>
{" "}
<FormButton onClick={afterSubmit} type="grey">
Annuler
</FormButton>{" "}
</>
)}
{children !== undefined && (
<>
{" "}
<IgnoreButton onClick={afterSubmit} text="Ignorer">
{children}
</IgnoreButton>{" "}
</>
)}
</div>
</div>
)
}
DayWishesForm.defaultProps = {
children: undefined,
afterSubmit: undefined,
}
export default memo(DayWishesForm)
// Fetch server-side data here
export const fetchFor = [fetchVolunteerDayWishesSetIfNeed]

View File

@ -24,6 +24,7 @@
}
.rightCol {
width: 100%;
text-align: center;
}
@ -37,7 +38,7 @@
text-align: left;
display: inline-block;
margin-bottom: 10px;
width: 180px;
width: 280px;
}
.dayWishesTitle {
@ -93,7 +94,7 @@
}
}
.dayWishesButtonWrapper {
.buttonWrapper {
margin-bottom: 10px;
text-align: center;
}

View File

@ -26,12 +26,30 @@ const ParticipationDetails: FC<Props> = (): JSX.Element | null => {
<div className={styles.title}>Mes infos logistiques</div>
{tshirtCount === 0 && (
<div className={styles.line}>
Je n'ai <b>aucun t-shirt</b> et je suis taillé·e <b>{tshirtSize}</b>
Je n'ai <b>aucun t-shirt</b> et{" "}
{tshirtSize ? (
<>
je suis taillé·e <b>{tshirtSize}</b>
</>
) : (
<>
ma taille est <span className={styles.lineEmpty}>non renseignées</span>
</>
)}
</div>
)}
{tshirtCount === 1 && (
<div className={styles.line}>
J'ai <b>un seul t-shirt</b> et je suis taillé·e <b>{tshirtSize}</b>
J'ai <b>un seul t-shirt</b> et{" "}
{tshirtSize ? (
<>
je suis taillé·e <b>{tshirtSize}</b>
</>
) : (
<>
ma taille est <span className={styles.lineEmpty}>non renseignées</span>
</>
)}
</div>
)}
{tshirtCount === 2 && (

View File

@ -17,6 +17,11 @@
margin-bottom: 5px;
}
.lineEmpty {
color: $color-red;
font-style: italic;
}
.editButton {
@include vertical-center();

View File

@ -1,19 +1,18 @@
import { FC, memo, useCallback, useEffect, useRef, useState } from "react"
import { FC, memo, ReactNode, useCallback, useEffect, useRef, useState } from "react"
import get from "lodash/get"
import set from "lodash/set"
import styles from "./styles.module.scss"
import {
foodDefaultValue,
tshirtSizes,
useUserParticipationDetails,
} from "../participationDetails.utils"
import { tshirtSizes, useUserParticipationDetails } from "../participationDetails.utils"
import FormButton from "../../Form/FormButton/FormButton"
import { fetchVolunteerParticipationDetailsSetIfNeed } from "../../../store/volunteerParticipationDetailsSet"
import IgnoreButton from "../../Form/IgnoreButton/IgnoreButton"
type Props = {
children?: ReactNode | undefined
afterSubmit?: () => void | undefined
}
const ParticipationDetailsForm: FC<Props> = ({ afterSubmit }): JSX.Element | null => {
const ParticipationDetailsForm: FC<Props> = ({ children, afterSubmit }): JSX.Element | null => {
const sizeRef = useRef<HTMLSelectElement | null>(null)
const dietRef = useRef<HTMLTextAreaElement | null>(null)
const [tshirtCountState, setTshirtCount] = useState<number>(0)
@ -23,7 +22,7 @@ const ParticipationDetailsForm: FC<Props> = ({ afterSubmit }): JSX.Element | nul
const onSubmit = useCallback(() => {
const tshirtSize = get(sizeRef, "current.value", "")
const food = get(dietRef, "current.value", "") || foodDefaultValue
const food = get(dietRef, "current.value", "")
saveParticipationDetails({
tshirtSize,
tshirtCount: tshirtCountState,
@ -103,13 +102,15 @@ const ParticipationDetailsForm: FC<Props> = ({ afterSubmit }): JSX.Element | nul
</label>
</div>
<div className={styles.rightCol}>
<select id="tshirtSize" ref={sizeRef} className={styles.tshirtCountSelect}>
{tshirtSizes.map((size) => (
<option key={size} value={size}>
{size}
</option>
))}
</select>
<label className={styles.tshirtSizeLabel}>
<select id="tshirtSize" ref={sizeRef} className={styles.tshirtCountSelect}>
{tshirtSizes.map((size) => (
<option key={size} value={size}>
{size}
</option>
))}
</select>
</label>
</div>
</div>
@ -143,17 +144,34 @@ const ParticipationDetailsForm: FC<Props> = ({ afterSubmit }): JSX.Element | nul
<textarea id="diet" ref={dietRef} placeholder="végétarien ? halal ? ..." />
</div>
<div className={styles.buttonWrapper}>
<FormButton onClick={onSubmit}>Enregistrer</FormButton>{" "}
<FormButton onClick={afterSubmit} type="grey">
Annuler
</FormButton>
<FormButton onClick={onSubmit}>Enregistrer</FormButton>
{children === undefined && (
<>
{" "}
<FormButton onClick={afterSubmit} type="grey">
Annuler
</FormButton>{" "}
</>
)}
{children !== undefined && (
<>
{" "}
<IgnoreButton onClick={afterSubmit} text="Ignorer">
{children}
</IgnoreButton>{" "}
</>
)}
</div>
</div>
)
}
ParticipationDetailsForm.defaultProps = {
children: undefined,
afterSubmit: undefined,
}
export default memo(ParticipationDetailsForm)
// Fetch server-side data here
export const fetchFor = [fetchVolunteerParticipationDetailsSetIfNeed]

View File

@ -27,6 +27,7 @@
}
.rightCol {
width: 100%;
text-align: center;
}
@ -42,13 +43,17 @@
text-align: left;
display: inline-block;
margin-bottom: 10px;
width: 180px;
width: 220px;
}
.tshirtCountSelect {
width: 120px;
.tshirtSizeLabel {
text-align: left;
display: inline-block;
margin-bottom: 10px;
margin-right: 60px;
width: 220px;
}
.tshirtSizeSelect {
border: 1px solid $color-grey-medium;
border-radius: 4px;
background-color: $color-white;
@ -59,12 +64,7 @@
text-align: left;
display: inline-block;
margin-bottom: 10px;
width: 180px;
}
.buttonWrapper {
margin-bottom: 10px;
text-align: center;
width: 220px;
}
.dietWrapper {
@ -83,3 +83,8 @@
outline: 0;
}
}
.buttonWrapper {
margin-bottom: 10px;
text-align: center;
}

View File

@ -1,4 +1,4 @@
import { FC, memo, useCallback, useEffect, useRef } from "react"
import { FC, memo, ReactNode, useCallback, useEffect, useRef } from "react"
import { useSelector } from "react-redux"
import get from "lodash/get"
import set from "lodash/set"
@ -9,12 +9,14 @@ import { fetchTeamListIfNeed, selectSortedActiveTeams } from "../../../store/tea
import useSelection from "../useSelection"
import { fetchVolunteerTeamWishesSetIfNeed } from "../../../store/volunteerTeamWishesSet"
import FormButton from "../../Form/FormButton/FormButton"
import IgnoreButton from "../../Form/IgnoreButton/IgnoreButton"
type Props = {
children?: ReactNode | undefined
afterSubmit?: () => void | undefined
}
const TeamWishesForm: FC<Props> = ({ afterSubmit }): JSX.Element | null => {
const TeamWishesForm: FC<Props> = ({ children, afterSubmit }): JSX.Element | null => {
const teams = useSelector(selectSortedActiveTeams)
const { selection, setSelection, toggleToSelection, isInSelection } = useSelection()
const commentRef = useRef<HTMLTextAreaElement | null>(null)
@ -35,7 +37,7 @@ const TeamWishesForm: FC<Props> = ({ afterSubmit }): JSX.Element | null => {
}, [selection, saveWishes, afterSubmit])
return (
<div className={styles.root}>
<div>
<div className={styles.title}>Mon choix d'équipe</div>
<div className={styles.intro}>
<p>
@ -73,7 +75,7 @@ const TeamWishesForm: FC<Props> = ({ afterSubmit }): JSX.Element | null => {
<textarea id="day-choice-comment" ref={commentRef} />
</div>
</div>
<div>
<div className={styles.rightCol}>
<ul className={styles.teamList}>
{teams.map((team: any) => (
<li
@ -96,16 +98,30 @@ const TeamWishesForm: FC<Props> = ({ afterSubmit }): JSX.Element | null => {
</div>
</div>
<div className={styles.buttonWrapper}>
<FormButton onClick={onSubmit}>Enregistrer</FormButton>{" "}
<FormButton onClick={afterSubmit} type="grey">
Annuler
</FormButton>
<FormButton onClick={onSubmit}>Enregistrer</FormButton>
{children === undefined && (
<>
{" "}
<FormButton onClick={afterSubmit} type="grey">
Annuler
</FormButton>{" "}
</>
)}
{children !== undefined && (
<>
{" "}
<IgnoreButton onClick={afterSubmit} text="Ignorer">
{children}
</IgnoreButton>{" "}
</>
)}
</div>
</div>
)
}
TeamWishesForm.defaultProps = {
children: undefined,
afterSubmit: undefined,
}

View File

@ -1,12 +1,6 @@
@import "../../../theme/variables";
@import "../../../theme/mixins";
.root {
@include desktop {
width: 680px;
}
}
.title {
padding: 4px;
font-weight: bold;
@ -31,10 +25,15 @@
}
.leftCol {
flex: 0 0 280px;
flex: 0 0 240px;
margin-right: 20px;
}
.rightCol {
width: 280px;
text-align: center;
}
.choiceList {
margin: 5px 0 15px;
padding: 0 0 0 30px;
@ -95,7 +94,7 @@
}
textarea {
width: 100%;
height: 50px;
height: 60px;
padding: 5px;
border: 1px solid $color-grey-light;
background-color: $color-grey-lighter;

View File

@ -1,9 +1,16 @@
import AnnouncementLink from "./AnnouncementLink"
import Board, { fetchFor as fetchForBoard } from "./VolunteerBoard/Board"
import DayWishesForm, {
fetchFor as fetchForDayWishesForm,
} from "./VolunteerBoard/DayWishesForm/DayWishesForm"
import ErrorBoundary from "./ErrorBoundary"
import GameList from "./GameList"
import Loading from "./Loading"
import LoginForm from "./LoginForm"
import Notifications, { fetchFor as fetchForNotifications } from "./Notifications"
import ParticipationDetailsForm, {
fetchFor as fetchForParticipationDetailsForm,
} from "./VolunteerBoard/ParticipationDetailsForm/ParticipationDetailsForm"
import PreRegisterForm from "./PreRegisterForm"
import TeamWishesForm, {
fetchFor as fetchForTeamWishesForm,
@ -12,16 +19,21 @@ 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,
Board,
fetchForBoard,
DayWishesForm,
fetchForDayWishesForm,
ErrorBoundary,
GameList,
Loading,
LoginForm,
Notifications,
fetchForNotifications,
ParticipationDetailsForm,
fetchForParticipationDetailsForm,
PreRegisterForm,
TeamWishesForm,
fetchForTeamWishesForm,
@ -29,5 +41,4 @@ export {
VolunteerList,
VolunteerSet,
WishAdd,
fetchForBoardForms,
}

View File

@ -5,10 +5,7 @@ import { useSelector } from "react-redux"
import { AppThunk } from "../../store"
import { selectUserJwtToken } from "../../store/auth"
import Page from "../../components/ui/Page/Page"
import Board from "../../components/VolunteerBoard/Board"
import { fetchVolunteerDayWishesSetIfNeed } from "../../store/volunteerDayWishesSet"
import { fetchVolunteerParticipationDetailsSetIfNeed } from "../../store/volunteerParticipationDetailsSet"
import { fetchForTeamWishesForm } from "../../components"
import { Board, fetchForBoard } from "../../components"
export type Props = RouteComponentProps
@ -27,10 +24,6 @@ const BoardPage: FC<Props> = (): JSX.Element => {
}
// Fetch server-side data here
export const loadData = (): AppThunk[] => [
fetchVolunteerDayWishesSetIfNeed(),
fetchVolunteerParticipationDetailsSetIfNeed(),
...fetchForTeamWishesForm.map((f) => f()),
]
export const loadData = (): AppThunk[] => [...fetchForBoard.map((f) => f())]
export default memo(BoardPage)

View File

@ -4,11 +4,10 @@ import { useSelector } from "react-redux"
import { Helmet } from "react-helmet"
import { AppThunk } from "../../store"
import { LoginForm, Notifications, fetchForBoardForms } from "../../components"
import { fetchForNotifications, LoginForm, Notifications } from "../../components"
import styles from "./styles.module.scss"
import { fetchVolunteerNotifsSetIfNeed } from "../../store/volunteerNotifsSet"
import { selectUserJwtToken } from "../../store/auth"
import Page from "../../components/ui/Page/Page"
export type Props = RouteComponentProps
@ -18,11 +17,7 @@ const HomePage: FC<Props> = (): JSX.Element => {
if (jwtToken === undefined) return <p>Loading...</p>
if (jwtToken) {
return (
<Page>
<Notifications />
</Page>
)
return <Notifications />
}
return (
<div>
@ -44,7 +39,7 @@ const HomePage: FC<Props> = (): JSX.Element => {
// Fetch server-side data here
export const loadData = (): AppThunk[] => [
fetchVolunteerNotifsSetIfNeed(),
...fetchForBoardForms.map((f) => f()),
...fetchForNotifications.map((f) => f()),
]
export default memo(HomePage)

View File

@ -22,8 +22,8 @@
@include flex-center;
}
@mixin page-content-wrapper($desktopWidth: 520px) {
margin: 10px;
@mixin page-content-wrapper($desktopWidth: 720px) {
margin: 10px 0;
padding: 10px;
background-color: $color-white;
border-radius: 15px;
@ -31,6 +31,7 @@
width: 100%;
@include desktop {
margin: 10px;
padding: 20px;
width: $desktopWidth;
}
@ -43,7 +44,7 @@
background-color: $color-grey-lighter;
}
@mixin nav-menu($desktopWidth: 520px) {
@mixin nav-menu($desktopWidth: 720px) {
margin: 0;
padding: 0;
list-style: none;
@ -53,7 +54,7 @@
}
}
@mixin nav-menu-item($desktopWidth: 520px) {
@mixin nav-menu-item($desktopWidth: 720px) {
display: inline-block;
margin: 0 4px;