mirror of
https://github.com/Paris-est-Ludique/intranet.git
synced 2025-06-08 08:34:20 +02:00
Add Brunch and Retex forms ans asks
This commit is contained in:
parent
bd7dc7578e
commit
72a633ae4f
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [17.x]
|
||||
node-version: [18.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
39
src/components/Asks/AskBrunch.tsx
Normal file
39
src/components/Asks/AskBrunch.tsx
Normal file
@ -0,0 +1,39 @@
|
||||
import { get } from "lodash"
|
||||
import { useCallback } from "react"
|
||||
import { fetchVolunteerAsksSet } from "../../store/volunteerAsksSet"
|
||||
import { useAskTools, addAsk, answerLaterOnProfile } from "./utils"
|
||||
import BrunchForm, { fetchFor as fetchForBrunchForm } from "../VolunteerBoard/BrunchForm/BrunchForm"
|
||||
import { useBrunch } from "../VolunteerBoard/brunch.utils"
|
||||
|
||||
export function AskBrunch(asks: JSX.Element[], id: number): void {
|
||||
const { dispatch, jwtToken, volunteerAsks } = useAskTools()
|
||||
|
||||
const onSubmit = useCallback((): void => {
|
||||
dispatch(
|
||||
fetchVolunteerAsksSet(jwtToken, 0, {
|
||||
hiddenAsks: [...(volunteerAsks?.hiddenAsks || []), id],
|
||||
})
|
||||
)
|
||||
}, [dispatch, id, jwtToken, volunteerAsks?.hiddenAsks])
|
||||
|
||||
const [retex] = useBrunch()
|
||||
const question1Default = "-1"
|
||||
const question1 = +get(retex, "question1", question1Default)
|
||||
const needToShow = !!retex && question1 === -1
|
||||
|
||||
addAsk(
|
||||
asks,
|
||||
id,
|
||||
volunteerAsks,
|
||||
false,
|
||||
needToShow,
|
||||
<>
|
||||
<BrunchForm afterSubmit={onSubmit}>{answerLaterOnProfile}</BrunchForm>
|
||||
Nous avons besoin d'une réponse avant le jeudi 15 soir minuit pour commander les repas !
|
||||
^^
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
// Fetch server-side data here
|
||||
export const fetchFor = [...fetchForBrunchForm]
|
59
src/components/Asks/AskRetex.tsx
Normal file
59
src/components/Asks/AskRetex.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
import { get } from "lodash"
|
||||
import { useCallback } from "react"
|
||||
import { fetchVolunteerAsksSet } from "../../store/volunteerAsksSet"
|
||||
import { useAskTools, addAsk, answerLaterOnProfile } from "./utils"
|
||||
import RetexForm, { fetchFor as fetchForRetexForm } from "../VolunteerBoard/RetexForm/RetexForm"
|
||||
import { useRetex } from "../VolunteerBoard/retex.utils"
|
||||
|
||||
export function AskRetex(asks: JSX.Element[], id: number): void {
|
||||
const { dispatch, jwtToken, volunteerAsks } = useAskTools()
|
||||
|
||||
const onSubmit = useCallback((): void => {
|
||||
dispatch(
|
||||
fetchVolunteerAsksSet(jwtToken, 0, {
|
||||
hiddenAsks: [...(volunteerAsks?.hiddenAsks || []), id],
|
||||
})
|
||||
)
|
||||
}, [dispatch, id, jwtToken, volunteerAsks?.hiddenAsks])
|
||||
|
||||
const [retex] = useRetex()
|
||||
const question1Default = "-1"
|
||||
const dayWishes = get(retex, "dayWishes", "")
|
||||
const question1 = +get(retex, "question1", question1Default)
|
||||
const question2 = get(retex, "question2", "")
|
||||
const question3 = get(retex, "question3", "")
|
||||
const question4 = get(retex, "question4", "")
|
||||
const question5 = get(retex, "question5", "")
|
||||
const question6 = get(retex, "question6", "")
|
||||
const question7 = get(retex, "question7", "")
|
||||
const question8 = get(retex, "question8", "")
|
||||
const question9 = get(retex, "question9", "")
|
||||
const wasHereBeforeAfter = dayWishes.match(/M|J|V|L/)
|
||||
const needToShow =
|
||||
!!retex &&
|
||||
(question1 === -1 ||
|
||||
!question2 ||
|
||||
!question3 ||
|
||||
!question4 ||
|
||||
(wasHereBeforeAfter && !question5) ||
|
||||
!question6 ||
|
||||
!question7 ||
|
||||
!question8 ||
|
||||
!question9)
|
||||
|
||||
addAsk(
|
||||
asks,
|
||||
id,
|
||||
volunteerAsks,
|
||||
false,
|
||||
needToShow,
|
||||
<>
|
||||
<RetexForm afterSubmit={onSubmit}>{answerLaterOnProfile}</RetexForm>
|
||||
Tes réponses sont modifiable sur la page <a href="/profil">Mon profil</a> jusqu'au 23
|
||||
septembre.
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
// Fetch server-side data here
|
||||
export const fetchFor = [...fetchForRetexForm]
|
@ -3,16 +3,18 @@ import React, { memo } from "react"
|
||||
import styles from "./styles.module.scss"
|
||||
import { useAskTools } from "./utils"
|
||||
import { AskWelcome } from "./AskWelcome"
|
||||
import { AskBrunch, fetchFor as fetchForBrunch } from "./AskBrunch"
|
||||
import { AskRetex, fetchFor as fetchForRetex } from "./AskRetex"
|
||||
import { AskDiscord, fetchFor as fetchForDiscord } from "./AskDiscord"
|
||||
import { AskDayWishes, fetchFor as fetchForDayWishes } from "./AskDayWishes"
|
||||
import { AskHosting, fetchFor as fetchForHosting } from "./AskHosting"
|
||||
// import { AskDayWishes, fetchFor as fetchForDayWishes } from "./AskDayWishes"
|
||||
// import { AskHosting, fetchFor as fetchForHosting } from "./AskHosting"
|
||||
// import { AskMeals, fetchFor as fetchForMeals } from "./AskMeals"
|
||||
import { AskPersonalInfo, fetchFor as fetchForPersonalInfo } from "./AskPersonalInfo"
|
||||
import { AskTeamWishes, fetchFor as fetchForTeamWishes } from "./AskTeamWishes"
|
||||
import {
|
||||
AskParticipationDetails,
|
||||
fetchFor as fetchForParticipationDetails,
|
||||
} from "./AskParticipationDetails"
|
||||
// import { AskPersonalInfo, fetchFor as fetchForPersonalInfo } from "./AskPersonalInfo"
|
||||
// import { AskTeamWishes, fetchFor as fetchForTeamWishes } from "./AskTeamWishes"
|
||||
// import {
|
||||
// AskParticipationDetails,
|
||||
// fetchFor as fetchForParticipationDetails,
|
||||
// } from "./AskParticipationDetails"
|
||||
import { AskPushNotif } from "./AskPushNotif"
|
||||
|
||||
const Asks = (): JSX.Element | null => {
|
||||
@ -20,13 +22,15 @@ const Asks = (): JSX.Element | null => {
|
||||
const asks: JSX.Element[] = []
|
||||
|
||||
AskWelcome(asks, 1)
|
||||
AskDiscord(asks, 3)
|
||||
AskBrunch(asks, 2)
|
||||
AskRetex(asks, 3)
|
||||
AskDiscord(asks, 5)
|
||||
|
||||
AskDayWishes(asks, 10)
|
||||
AskTeamWishes(asks, 11)
|
||||
AskParticipationDetails(asks, 12)
|
||||
AskPersonalInfo(asks, 15)
|
||||
AskHosting(asks, 20)
|
||||
// AskDayWishes(asks, 10)
|
||||
// AskTeamWishes(asks, 11)
|
||||
// AskParticipationDetails(asks, 12)
|
||||
// AskPersonalInfo(asks, 15)
|
||||
// AskHosting(asks, 20)
|
||||
// AskMeals(asks, 22)
|
||||
|
||||
AskPushNotif(asks, 99)
|
||||
@ -62,11 +66,13 @@ export default memo(Asks)
|
||||
|
||||
// Fetch server-side data here
|
||||
export const fetchFor = [
|
||||
...fetchForBrunch,
|
||||
...fetchForRetex,
|
||||
...fetchForDiscord,
|
||||
...fetchForDayWishes,
|
||||
...fetchForHosting,
|
||||
// ...fetchForDayWishes,
|
||||
// ...fetchForHosting,
|
||||
// ...fetchForMeals,
|
||||
...fetchForTeamWishes,
|
||||
...fetchForParticipationDetails,
|
||||
...fetchForPersonalInfo,
|
||||
// ...fetchForTeamWishes,
|
||||
// ...fetchForParticipationDetails,
|
||||
// ...fetchForPersonalInfo,
|
||||
]
|
||||
|
@ -37,7 +37,7 @@ const FormButton: FC<Props> = ({ children, text, onClick }): JSX.Element => {
|
||||
</button>
|
||||
<div>{children}</div>
|
||||
<button type="button" className={styles.greyButton} onClick={onIgnore}>
|
||||
Ok, ignorer
|
||||
Vraiment passer au questionnaire suivant
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
@ -52,7 +52,7 @@ const MainMenu: FC = (): JSX.Element => {
|
||||
<MenuItem name="Questions" pathname="/" />
|
||||
<MenuItem name="Annonces" pathname="/annonces" />
|
||||
<MenuItem name="Mon profil" pathname="/profil" />
|
||||
<MenuItem name="Mes connaissances" pathname="/connaissances" />
|
||||
{/* <MenuItem name="Mes connaissances" pathname="/connaissances" /> */}
|
||||
<RestrictMenuItem
|
||||
role={ROLES.ASSIGNER}
|
||||
name="Gestion équipes"
|
||||
|
@ -709,7 +709,7 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
|
||||
</div>
|
||||
<div className={styles.rightCol}>
|
||||
<div className={styles.rightColContainer}>
|
||||
{["WhatsApp", "Signal", "SMS", "Email", "Appel", "Aucun"].map((option) => (
|
||||
{["WhatsApp", "Signal", "SMS", "Email", "Aucun"].map((option) => (
|
||||
<label className={styles.shortAnswerLabel} key={option}>
|
||||
<input
|
||||
type="radio"
|
||||
|
@ -1,32 +1,45 @@
|
||||
import { FC, memo } from "react"
|
||||
import DayWishes from "./DayWishes/DayWishes"
|
||||
import DayWishesFormModal from "./DayWishesForm/DayWishesFormModal"
|
||||
import Hosting from "./Hosting/Hosting"
|
||||
import HostingFormModal from "./HostingForm/HostingFormModal"
|
||||
import Meals from "./Meals/Meals"
|
||||
import MealsFormModal from "./MealsForm/MealsFormModal"
|
||||
import ParticipationDetails from "./ParticipationDetails/ParticipationDetails"
|
||||
import ParticipationDetailsFormModal from "./ParticipationDetailsForm/ParticipationDetailsFormModal"
|
||||
import TeamWishes from "./TeamWishes/TeamWishes"
|
||||
import TeamWishesFormModal from "./TeamWishesForm/TeamWishesFormModal"
|
||||
// import DayWishes from "./DayWishes/DayWishes"
|
||||
// import DayWishesFormModal from "./DayWishesForm/DayWishesFormModal"
|
||||
// import Hosting from "./Hosting/Hosting"
|
||||
// import HostingFormModal from "./HostingForm/HostingFormModal"
|
||||
// import Meals from "./Meals/Meals"
|
||||
// import MealsFormModal from "./MealsForm/MealsFormModal"
|
||||
// import ParticipationDetails from "./ParticipationDetails/ParticipationDetails"
|
||||
// import ParticipationDetailsFormModal from "./ParticipationDetailsForm/ParticipationDetailsFormModal"
|
||||
// import TeamWishes from "./TeamWishes/TeamWishes"
|
||||
// import TeamWishesFormModal from "./TeamWishesForm/TeamWishesFormModal"
|
||||
// import VolunteerTeam from "./VolunteerTeam/VolunteerTeam"
|
||||
import withUserConnected from "../../utils/withUserConnected"
|
||||
import ContentTitle from "../ui/Content/ContentTitle"
|
||||
import { fetchFor as fetchForDayWishesForm } from "./DayWishesForm/DayWishesForm"
|
||||
import { fetchFor as fetchForHostingForm } from "./HostingForm/HostingForm"
|
||||
import { fetchFor as fetchForMealsForm } from "./MealsForm/MealsForm"
|
||||
import { fetchFor as fetchForParticipationDetailsForm } from "./ParticipationDetailsForm/ParticipationDetailsForm"
|
||||
// import { fetchFor as fetchForDayWishesForm } from "./DayWishesForm/DayWishesForm"
|
||||
// import { fetchFor as fetchForHostingForm } from "./HostingForm/HostingForm"
|
||||
// import { fetchFor as fetchForMealsForm } from "./MealsForm/MealsForm"
|
||||
// import { fetchFor as fetchForParticipationDetailsForm } from "./ParticipationDetailsForm/ParticipationDetailsForm"
|
||||
// import { fetchFor as fetchForTeamWishesForm } from "./TeamWishesForm/TeamWishesForm"
|
||||
import { fetchFor as fetchForPersonalInfoForm } from "./PersonalInfoForm/PersonalInfoForm"
|
||||
import { fetchFor as fetchForTeamWishesForm } from "./TeamWishesForm/TeamWishesForm"
|
||||
import VolunteerTeam from "./VolunteerTeam/VolunteerTeam"
|
||||
import PersonalInfo from "./PersonalInfo/PersonalInfo"
|
||||
import PersonalInfoFormModal from "./PersonalInfoForm/PersonalInfoFormModal"
|
||||
import Brunch from "./Brunch/Brunch"
|
||||
import BrunchFormModal from "./BrunchForm/BrunchFormModal"
|
||||
import { fetchFor as fetchForBrunchForm } from "./BrunchForm/BrunchForm"
|
||||
import Retex from "./Retex/Retex"
|
||||
import RetexFormModal from "./RetexForm/RetexFormModal"
|
||||
import { fetchFor as fetchForRetexForm } from "./RetexForm/RetexForm"
|
||||
import { useRetex } from "./retex.utils"
|
||||
|
||||
const Board: FC = (): JSX.Element => (
|
||||
<>
|
||||
<ContentTitle title="Profil spécifique au festival" />
|
||||
<PersonalInfo />
|
||||
<PersonalInfoFormModal />
|
||||
<DayWishes />
|
||||
const Board: FC = (): JSX.Element => {
|
||||
const [retex] = useRetex()
|
||||
return (
|
||||
<>
|
||||
<ContentTitle title="Profil spécifique au festival" />
|
||||
<PersonalInfo />
|
||||
<PersonalInfoFormModal />
|
||||
{retex && <Brunch />}
|
||||
{retex && <BrunchFormModal />}
|
||||
{retex && <Retex />}
|
||||
{retex && <RetexFormModal />}
|
||||
{/* <DayWishes />
|
||||
<DayWishesFormModal />
|
||||
<ParticipationDetails />
|
||||
<ParticipationDetailsFormModal />
|
||||
@ -36,17 +49,20 @@ const Board: FC = (): JSX.Element => (
|
||||
<Hosting />
|
||||
<HostingFormModal />
|
||||
<Meals />
|
||||
<MealsFormModal />
|
||||
</>
|
||||
)
|
||||
<MealsFormModal /> */}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(withUserConnected(Board))
|
||||
|
||||
export const fetchFor = [
|
||||
...fetchForDayWishesForm,
|
||||
...fetchForHostingForm,
|
||||
...fetchForMealsForm,
|
||||
...fetchForParticipationDetailsForm,
|
||||
...fetchForPersonalInfoForm,
|
||||
...fetchForTeamWishesForm,
|
||||
...fetchForRetexForm,
|
||||
...fetchForBrunchForm,
|
||||
// ...fetchForDayWishesForm,
|
||||
// ...fetchForHostingForm,
|
||||
// ...fetchForMealsForm,
|
||||
// ...fetchForParticipationDetailsForm,
|
||||
// ...fetchForTeamWishesForm,
|
||||
]
|
||||
|
42
src/components/VolunteerBoard/Brunch/Brunch.tsx
Normal file
42
src/components/VolunteerBoard/Brunch/Brunch.tsx
Normal file
@ -0,0 +1,42 @@
|
||||
import { FC, memo, useCallback } from "react"
|
||||
import get from "lodash/get"
|
||||
import styles from "./styles.module.scss"
|
||||
import { useBrunch } from "../brunch.utils"
|
||||
import { displayModal, MODAL_IDS } from "../../../store/ui"
|
||||
import useAction from "../../../utils/useAction"
|
||||
|
||||
type Props = {
|
||||
afterSubmit?: () => void | undefined
|
||||
}
|
||||
|
||||
const Brunch: FC<Props> = (): JSX.Element | null => {
|
||||
const [retex] = useBrunch()
|
||||
const question1 = get(retex, "question1", -1)
|
||||
const execDisplayModal = useAction(displayModal)
|
||||
const onEdit = useCallback(() => execDisplayModal(MODAL_IDS.BRUNCH), [execDisplayModal])
|
||||
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<div className={styles.title}>Inscription au brunch</div>
|
||||
<div className={styles.line}>
|
||||
{question1 === -1 && (
|
||||
<>
|
||||
Ma présence au brunch est{" "}
|
||||
<span className={styles.lineEmpty}>non renseignée</span>
|
||||
</>
|
||||
)}
|
||||
{question1 === 0 && <>Je ne viendrai pas au brunch </>}
|
||||
{question1 > 0 && (
|
||||
<>Je viendrai au brunch{question1 > 1 && <> avec {question1} personne(s)</>}</>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.editButton}>
|
||||
<button type="button" onClick={onEdit}>
|
||||
Modifier
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(Brunch)
|
36
src/components/VolunteerBoard/Brunch/styles.module.scss
Executable file
36
src/components/VolunteerBoard/Brunch/styles.module.scss
Executable file
@ -0,0 +1,36 @@
|
||||
@import "../../../theme/variables";
|
||||
@import "../../../theme/mixins";
|
||||
|
||||
.root {
|
||||
@include inner-content-wrapper();
|
||||
|
||||
position: relative;
|
||||
padding-right: 90px;
|
||||
}
|
||||
|
||||
.title {
|
||||
padding-bottom: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.line {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.lineEmpty {
|
||||
color: $color-red;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.editButton {
|
||||
@include vertical-center();
|
||||
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
|
||||
button {
|
||||
color: $color-green;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
142
src/components/VolunteerBoard/BrunchForm/BrunchForm.tsx
Normal file
142
src/components/VolunteerBoard/BrunchForm/BrunchForm.tsx
Normal file
@ -0,0 +1,142 @@
|
||||
import { FC, memo, ReactNode, useCallback, useEffect, useRef, useState } from "react"
|
||||
import classnames from "classnames"
|
||||
import get from "lodash/get"
|
||||
import set from "lodash/set"
|
||||
import styles from "./styles.module.scss"
|
||||
import { useBrunch } from "../brunch.utils"
|
||||
import FormButton from "../../Form/FormButton/FormButton"
|
||||
import { fetchRetexSetIfNeed } from "../../../store/retexSet"
|
||||
import IgnoreButton from "../../Form/IgnoreButton/IgnoreButton"
|
||||
|
||||
type Props = {
|
||||
children?: ReactNode | undefined
|
||||
afterSubmit?: () => void | undefined
|
||||
}
|
||||
|
||||
const BrunchForm: FC<Props> = ({ children, afterSubmit }): JSX.Element | null => {
|
||||
const [brunchPresence, setBrunchPresence] = useState<string>("inconnue")
|
||||
const question1Ref = useRef<HTMLInputElement | null>(null)
|
||||
const question1Default = "-1"
|
||||
|
||||
const [retex, saveBrunch] = useBrunch()
|
||||
|
||||
const onSubmit = useCallback(() => {
|
||||
if (!retex) {
|
||||
return
|
||||
}
|
||||
const question1 = +get(question1Ref, "current.value", question1Default)
|
||||
saveBrunch(retex.id, question1)
|
||||
if (afterSubmit) afterSubmit()
|
||||
}, [afterSubmit, retex, saveBrunch])
|
||||
|
||||
const onBrunchPresenceChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const presence = e.target.value
|
||||
setBrunchPresence(presence)
|
||||
const guestCountDefault = { oui: 1, non: 0, "peut-etre": -1 }[presence]
|
||||
set(question1Ref, "current.value", `${guestCountDefault}`)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const question1 = +get(retex, "question1", question1Default)
|
||||
|
||||
set(question1Ref, "current.value", `${question1}`)
|
||||
if (question1 >= 1) setBrunchPresence("oui")
|
||||
else if (question1 === 0) setBrunchPresence("non")
|
||||
else setBrunchPresence("peut-etre")
|
||||
}, [setBrunchPresence, retex])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.title}>Inscription au brunch</div>
|
||||
<div className={styles.inputWrapper}>
|
||||
<div className={styles.leftCol}>
|
||||
<div className={styles.brunchTitle}>
|
||||
Viendras-tu au brunch samedi 1er octobre ? Le boulodrome (19 route des
|
||||
fortifications) nous sera réservé de 10h à 18h.
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.rightCol}>
|
||||
<label className={styles.brunchLabel}>
|
||||
<input
|
||||
type="radio"
|
||||
value="oui"
|
||||
name="brunchPresence"
|
||||
onChange={onBrunchPresenceChange}
|
||||
checked={brunchPresence === "oui"}
|
||||
/>{" "}
|
||||
Oui
|
||||
</label>
|
||||
<label className={styles.brunchLabel}>
|
||||
<input
|
||||
type="radio"
|
||||
value="non"
|
||||
name="brunchPresence"
|
||||
onChange={onBrunchPresenceChange}
|
||||
checked={brunchPresence === "non"}
|
||||
/>{" "}
|
||||
Non
|
||||
</label>
|
||||
<label className={styles.brunchLabel}>
|
||||
<input
|
||||
type="radio"
|
||||
value="peut-etre"
|
||||
name="brunchPresence"
|
||||
onChange={onBrunchPresenceChange}
|
||||
checked={brunchPresence === "peut-etre"}
|
||||
/>{" "}
|
||||
Je ne sais pas encore
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={classnames(
|
||||
styles.inputWrapper,
|
||||
brunchPresence === "oui" ? null : styles.invisible
|
||||
)}
|
||||
>
|
||||
<div className={styles.leftCol}>
|
||||
<div className={styles.guestCountTitle}>
|
||||
À combien viendras-tu ? Le brunch est pour les bénévoles ayant participé à
|
||||
la dernière édition (y compris ceux empêchés à la dernière minute), ainsi
|
||||
qu'aux éventuels +1 conjoints et enfants. Mais pas les amis, pour lesquels
|
||||
d'autres rdv seront proposés au cours de l'année.
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.rightCol}>
|
||||
<input className={styles.guestCountLabel} type="text" ref={question1Ref} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.buttonWrapper}>
|
||||
<FormButton onClick={onSubmit}>Enregistrer</FormButton>
|
||||
{children === undefined && (
|
||||
<>
|
||||
{" "}
|
||||
<FormButton onClick={afterSubmit} type="grey">
|
||||
Annuler
|
||||
</FormButton>{" "}
|
||||
</>
|
||||
)}
|
||||
{children !== undefined && (
|
||||
<>
|
||||
{" "}
|
||||
<IgnoreButton onClick={afterSubmit} text="Ignorer pour l'instant">
|
||||
{children}
|
||||
</IgnoreButton>{" "}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
BrunchForm.defaultProps = {
|
||||
children: undefined,
|
||||
afterSubmit: undefined,
|
||||
}
|
||||
|
||||
export default memo(BrunchForm)
|
||||
|
||||
// Fetch server-side data here
|
||||
export const fetchFor = [fetchRetexSetIfNeed]
|
18
src/components/VolunteerBoard/BrunchForm/BrunchFormModal.tsx
Normal file
18
src/components/VolunteerBoard/BrunchForm/BrunchFormModal.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { FC, memo, useCallback } from "react"
|
||||
import { hideModal, MODAL_IDS } from "../../../store/ui"
|
||||
import Modal from "../../Modal/Modal"
|
||||
import useAction from "../../../utils/useAction"
|
||||
import BrunchForm from "./BrunchForm"
|
||||
|
||||
const BrunchFormModal: FC = (): JSX.Element => {
|
||||
const execHideModal = useAction(hideModal)
|
||||
const afterFormSubmit = useCallback(() => execHideModal(), [execHideModal])
|
||||
|
||||
return (
|
||||
<Modal modalId={MODAL_IDS.BRUNCH}>
|
||||
<BrunchForm afterSubmit={afterFormSubmit} />
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(BrunchFormModal)
|
109
src/components/VolunteerBoard/BrunchForm/styles.module.scss
Executable file
109
src/components/VolunteerBoard/BrunchForm/styles.module.scss
Executable file
@ -0,0 +1,109 @@
|
||||
@import "../../../theme/variables";
|
||||
@import "../../../theme/mixins";
|
||||
|
||||
.title {
|
||||
padding: 4px;
|
||||
margin: 15px 0;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.inputWrapper {
|
||||
margin: 25px 0;
|
||||
|
||||
@include desktop {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
min-width: 175px;
|
||||
border: 1px solid $color-grey-medium;
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.leftCol {
|
||||
flex: 0 0 240px;
|
||||
}
|
||||
|
||||
.rightCol {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.brunchTitle {
|
||||
display: inline-block;
|
||||
width: 320px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.invisible {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.brunchLabel {
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
width: 280px;
|
||||
}
|
||||
|
||||
.brunchPresenceTitle {
|
||||
display: inline-block;
|
||||
width: 320px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.brunchPresenceLabel {
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.guestCountTitle {
|
||||
display: inline-block;
|
||||
width: 320px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.guestCountLabel {
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.questionWrapper {
|
||||
margin: 6px 0 14px;
|
||||
|
||||
label {
|
||||
display: block;
|
||||
padding: 6px 0 2px 4px;
|
||||
}
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: 75px;
|
||||
padding: 5px;
|
||||
border: 1px solid $color-grey-light;
|
||||
background-color: $color-grey-lighter;
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.buttonWrapper {
|
||||
margin-bottom: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.preAnswerWrapper {
|
||||
margin-bottom: 3px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.preAnswerButton {
|
||||
@include form-button;
|
||||
|
||||
margin-left: 2px;
|
||||
margin-top: 2px;
|
||||
}
|
@ -164,7 +164,7 @@ const DayWishesForm: FC<Props> = ({ children, afterSubmit }): JSX.Element => {
|
||||
{children !== undefined && (
|
||||
<>
|
||||
{" "}
|
||||
<IgnoreButton onClick={afterSubmit} text="Ignorer">
|
||||
<IgnoreButton onClick={afterSubmit} text="Ignorer pour l'instant">
|
||||
{children}
|
||||
</IgnoreButton>{" "}
|
||||
</>
|
||||
|
@ -112,7 +112,7 @@ const HostingForm: FC<Props> = ({ children, afterSubmit }): JSX.Element => {
|
||||
{children !== undefined && (
|
||||
<>
|
||||
{" "}
|
||||
<IgnoreButton onClick={afterSubmit} text="Ignorer">
|
||||
<IgnoreButton onClick={afterSubmit} text="Ignorer pour l'instant">
|
||||
{children}
|
||||
</IgnoreButton>{" "}
|
||||
</>
|
||||
|
@ -135,7 +135,7 @@ const MealsForm: FC<Props> = ({ children, afterSubmit }): JSX.Element => {
|
||||
{children !== undefined && (
|
||||
<>
|
||||
{" "}
|
||||
<IgnoreButton onClick={afterSubmit} text="Ignorer">
|
||||
<IgnoreButton onClick={afterSubmit} text="Ignorer pour l'instant">
|
||||
{children}
|
||||
</IgnoreButton>{" "}
|
||||
</>
|
||||
|
@ -37,32 +37,6 @@
|
||||
width: 220px;
|
||||
}
|
||||
|
||||
.canHostCountTitle {
|
||||
display: inline-block;
|
||||
width: 320px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.canHostCountLabel {
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.distanceToFestivalTitle {
|
||||
display: inline-block;
|
||||
width: 320px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.distanceToFestivalLabel {
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.mealsTitle {
|
||||
display: inline-block;
|
||||
width: 320px;
|
||||
|
@ -151,7 +151,7 @@ const ParticipationDetailsForm: FC<Props> = ({ children, afterSubmit }): JSX.Ele
|
||||
{children !== undefined && (
|
||||
<>
|
||||
{" "}
|
||||
<IgnoreButton onClick={afterSubmit} text="Ignorer">
|
||||
<IgnoreButton onClick={afterSubmit} text="Ignorer pour l'instant">
|
||||
{children}
|
||||
</IgnoreButton>{" "}
|
||||
</>
|
||||
|
@ -96,7 +96,6 @@ const PersonalInfoForm: FC<Props> = ({ children, afterSubmit }): JSX.Element =>
|
||||
type="file"
|
||||
name="myImage"
|
||||
onChange={(event) => {
|
||||
console.log(event?.target?.files?.[0])
|
||||
setSelectedImage(event?.target?.files?.[0] || null)
|
||||
}}
|
||||
/>
|
||||
@ -134,7 +133,7 @@ const PersonalInfoForm: FC<Props> = ({ children, afterSubmit }): JSX.Element =>
|
||||
{children !== undefined && (
|
||||
<>
|
||||
{" "}
|
||||
<IgnoreButton onClick={afterSubmit} text="Ignorer">
|
||||
<IgnoreButton onClick={afterSubmit} text="Ignorer pour l'instant">
|
||||
{children}
|
||||
</IgnoreButton>{" "}
|
||||
</>
|
||||
|
56
src/components/VolunteerBoard/Retex/Retex.tsx
Normal file
56
src/components/VolunteerBoard/Retex/Retex.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
import { FC, memo, useCallback } from "react"
|
||||
import get from "lodash/get"
|
||||
import styles from "./styles.module.scss"
|
||||
import { useRetex } from "../retex.utils"
|
||||
import { displayModal, MODAL_IDS } from "../../../store/ui"
|
||||
import useAction from "../../../utils/useAction"
|
||||
|
||||
type Props = {
|
||||
afterSubmit?: () => void | undefined
|
||||
}
|
||||
|
||||
const Retex: FC<Props> = (): JSX.Element | null => {
|
||||
const [retex] = useRetex()
|
||||
const dayWishes = get(retex, "dayWishes", "")
|
||||
const question2 = get(retex, "question2", "")
|
||||
const question3 = get(retex, "question3", "")
|
||||
const question4 = get(retex, "question4", "")
|
||||
const question5 = get(retex, "question5", "")
|
||||
const question6 = get(retex, "question6", "")
|
||||
const question7 = get(retex, "question7", "")
|
||||
const question8 = get(retex, "question8", "")
|
||||
const execDisplayModal = useAction(displayModal)
|
||||
const onEdit = useCallback(() => execDisplayModal(MODAL_IDS.RETEX), [execDisplayModal])
|
||||
const weekDays = `,${dayWishes}`.replace(/,S|,D/g, "").replace(/^,/, "")
|
||||
const answeredQuestionCount =
|
||||
(question2 ? 1 : 0) +
|
||||
(question3 ? 1 : 0) +
|
||||
(question4 ? 1 : 0) +
|
||||
(question5 ? 1 : 0) +
|
||||
(question6 ? 1 : 0) +
|
||||
(question7 ? 1 : 0) +
|
||||
(question8 ? 1 : 0)
|
||||
const expectedAnswerCount = weekDays ? 7 : 6
|
||||
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<div className={styles.title}>Retour sur PeL 2022</div>
|
||||
<div className={styles.line}>
|
||||
<span
|
||||
className={answeredQuestionCount < expectedAnswerCount ? styles.lineEmpty : ""}
|
||||
>
|
||||
{answeredQuestionCount} réponses{" "}
|
||||
</span>
|
||||
sur {expectedAnswerCount}
|
||||
{answeredQuestionCount >= expectedAnswerCount && <> !</>}
|
||||
</div>
|
||||
<div className={styles.editButton}>
|
||||
<button type="button" onClick={onEdit}>
|
||||
Modifier
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(Retex)
|
36
src/components/VolunteerBoard/Retex/styles.module.scss
Executable file
36
src/components/VolunteerBoard/Retex/styles.module.scss
Executable file
@ -0,0 +1,36 @@
|
||||
@import "../../../theme/variables";
|
||||
@import "../../../theme/mixins";
|
||||
|
||||
.root {
|
||||
@include inner-content-wrapper();
|
||||
|
||||
position: relative;
|
||||
padding-right: 90px;
|
||||
}
|
||||
|
||||
.title {
|
||||
padding-bottom: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.line {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.lineEmpty {
|
||||
color: $color-red;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.editButton {
|
||||
@include vertical-center();
|
||||
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
|
||||
button {
|
||||
color: $color-green;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
374
src/components/VolunteerBoard/RetexForm/RetexForm.tsx
Normal file
374
src/components/VolunteerBoard/RetexForm/RetexForm.tsx
Normal file
@ -0,0 +1,374 @@
|
||||
import { FC, memo, ReactNode, useCallback, useEffect, useRef } from "react"
|
||||
import get from "lodash/get"
|
||||
import set from "lodash/set"
|
||||
import styles from "./styles.module.scss"
|
||||
import { useRetex } from "../retex.utils"
|
||||
import FormButton from "../../Form/FormButton/FormButton"
|
||||
import { fetchRetexSetIfNeed } from "../../../store/retexSet"
|
||||
import IgnoreButton from "../../Form/IgnoreButton/IgnoreButton"
|
||||
|
||||
type Props = {
|
||||
children?: ReactNode | undefined
|
||||
afterSubmit?: () => void | undefined
|
||||
}
|
||||
|
||||
const RetexForm: FC<Props> = ({ children, afterSubmit }): JSX.Element | null => {
|
||||
const question2Ref = useRef<HTMLTextAreaElement | null>(null)
|
||||
const question3Ref = useRef<HTMLTextAreaElement | null>(null)
|
||||
const question4Ref = useRef<HTMLTextAreaElement | null>(null)
|
||||
const question5Ref = useRef<HTMLTextAreaElement | null>(null)
|
||||
const question6Ref = useRef<HTMLTextAreaElement | null>(null)
|
||||
const question7Ref = useRef<HTMLTextAreaElement | null>(null)
|
||||
const question8Ref = useRef<HTMLTextAreaElement | null>(null)
|
||||
const question9Ref = useRef<HTMLTextAreaElement | null>(null)
|
||||
|
||||
const [retex, saveRetex] = useRetex()
|
||||
const dayWishes = get(retex, "dayWishes", "")
|
||||
const weekDaysText = `,${dayWishes}`
|
||||
.replace(/,S|,D/g, "")
|
||||
.replace("M", "mercredi")
|
||||
.replace("J", "jeudi")
|
||||
.replace("V", "vendredi")
|
||||
.replace("L", "lundi")
|
||||
.replace(/^,/, "")
|
||||
const weekDays = weekDaysText ? weekDaysText.split(",") : []
|
||||
|
||||
const onSubmit = useCallback(() => {
|
||||
if (!retex) {
|
||||
return
|
||||
}
|
||||
const question2 = get(question2Ref, "current.value", "")
|
||||
const question3 = get(question3Ref, "current.value", "")
|
||||
const question4 = get(question4Ref, "current.value", "")
|
||||
const question5 = get(question5Ref, "current.value", "")
|
||||
const question6 = get(question6Ref, "current.value", "")
|
||||
const question7 = get(question7Ref, "current.value", "")
|
||||
const question8 = get(question8Ref, "current.value", "")
|
||||
const question9 = get(question9Ref, "current.value", "")
|
||||
saveRetex(
|
||||
retex.id,
|
||||
question2,
|
||||
question3,
|
||||
question4,
|
||||
question5,
|
||||
question6,
|
||||
question7,
|
||||
question8,
|
||||
question9
|
||||
)
|
||||
if (afterSubmit) afterSubmit()
|
||||
}, [afterSubmit, retex, saveRetex])
|
||||
|
||||
const preAnswer = useCallback(
|
||||
(ref: React.MutableRefObject<HTMLTextAreaElement | null>) =>
|
||||
(e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
const answer = e.currentTarget.innerText
|
||||
const currentAnswer = get(ref, "current.value", "")
|
||||
const newAnswer = `${currentAnswer}${currentAnswer ? "\n" : ""}${answer.replace(
|
||||
/_+$/,
|
||||
""
|
||||
)}`
|
||||
set(ref, "current.value", newAnswer)
|
||||
ref?.current?.focus()
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const question2 = get(retex, "question2", "")
|
||||
const question3 = get(retex, "question3", "")
|
||||
const question4 = get(retex, "question4", "")
|
||||
const question5 = get(retex, "question5", "")
|
||||
const question6 = get(retex, "question6", "")
|
||||
const question7 = get(retex, "question7", "")
|
||||
const question8 = get(retex, "question8", "")
|
||||
const question9 = get(retex, "question9", "")
|
||||
|
||||
if (question2) set(question2Ref, "current.value", question2)
|
||||
if (question3) set(question3Ref, "current.value", question3)
|
||||
if (question4) set(question4Ref, "current.value", question4)
|
||||
if (question5) set(question5Ref, "current.value", question5)
|
||||
if (question6) set(question6Ref, "current.value", question6)
|
||||
if (question7) set(question7Ref, "current.value", question7)
|
||||
if (question8) set(question8Ref, "current.value", question8)
|
||||
if (question9) set(question9Ref, "current.value", question9)
|
||||
}, [retex])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.title}>Retour sur PeL 2022</div>
|
||||
|
||||
<div className={styles.questionWrapper}>
|
||||
On aimerait savoir comment s'est passé ton festival !<br />
|
||||
L'orga lira tes réponses et répondra à son tour à tes questions soit
|
||||
individuellement, soit dans la prochaine gazette. Puis toutes les réponses seront
|
||||
anonymisées et une synthèse sera diffusée dans la gazette.
|
||||
<br />
|
||||
Tu peux répondre par 3 mots ou 3 pages, on a tous vécu le festival différemment !
|
||||
</div>
|
||||
|
||||
<div className={styles.questionWrapper}>
|
||||
<label htmlFor="question2">
|
||||
As-tu un avis positif/négatif ou une recommandation au sujet des repas, des WC,
|
||||
de tes besoins en tee-shirt, chapeau, eau, bière, crème solaire ?
|
||||
</label>
|
||||
<div className={styles.preAnswerWrapper}>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.preAnswerButton}
|
||||
onClick={preAnswer(question2Ref)}
|
||||
>
|
||||
Tout était top.
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.preAnswerButton}
|
||||
onClick={preAnswer(question2Ref)}
|
||||
>
|
||||
On m’a rapporté que __
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.preAnswerButton}
|
||||
onClick={preAnswer(question2Ref)}
|
||||
>
|
||||
J’ai eu un problème concernant __
|
||||
</button>
|
||||
</div>
|
||||
<textarea id="question2" ref={question2Ref} placeholder="..." />
|
||||
</div>
|
||||
|
||||
<div className={styles.questionWrapper}>
|
||||
<label htmlFor="question3">
|
||||
As-tu, comme prévu et en accord avec ton équipe, pris du temps pour profiter des
|
||||
espaces visiteurs du festival, des badges inter-équipes, des crêpes/glaces à
|
||||
l’espace bénévole ?
|
||||
</label>
|
||||
<div className={styles.preAnswerWrapper}>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.preAnswerButton}
|
||||
onClick={preAnswer(question3Ref)}
|
||||
>
|
||||
J’ai joué X temps sur des stands éditeurs.
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.preAnswerButton}
|
||||
onClick={preAnswer(question3Ref)}
|
||||
>
|
||||
Je me suis baladé sans jouer.
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.preAnswerButton}
|
||||
onClick={preAnswer(question3Ref)}
|
||||
>
|
||||
Il y avait des crêpes ?
|
||||
</button>
|
||||
</div>
|
||||
<textarea id="question3" ref={question3Ref} placeholder="..." />
|
||||
</div>
|
||||
|
||||
<div className={styles.questionWrapper}>
|
||||
<label htmlFor="question4">
|
||||
Gestion du stress, de la fatigue : as-tu fait des pauses (à notre espace sieste,
|
||||
à l’espace Shiatsu) ? T’es-tu senti épuisé lors du festival, après, et sais-tu
|
||||
pourquoi ?
|
||||
</label>
|
||||
<div className={styles.preAnswerWrapper}>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.preAnswerButton}
|
||||
onClick={preAnswer(question4Ref)}
|
||||
>
|
||||
J’étais en forme tout du long.
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.preAnswerButton}
|
||||
onClick={preAnswer(question4Ref)}
|
||||
>
|
||||
Les jours de préparatifs m’ont fatigués.
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.preAnswerButton}
|
||||
onClick={preAnswer(question4Ref)}
|
||||
>
|
||||
Il y avait des massages gratuits ?
|
||||
</button>
|
||||
</div>
|
||||
<textarea id="question4" ref={question4Ref} placeholder="..." />
|
||||
</div>
|
||||
|
||||
{weekDays.length > 0 && (
|
||||
<div className={styles.questionWrapper}>
|
||||
<label htmlFor="question5">
|
||||
{weekDays.length > 1
|
||||
? `Comment se sont passés les ${weekDays.join(", ")} ?`
|
||||
: `Comment s'est passé le ${weekDays.join(", ")} ?`}{" "}
|
||||
As-tu eu assez d’infos, te sentais-tu utile ? Y compris au sein de ton
|
||||
équipe ?
|
||||
</label>
|
||||
<div className={styles.preAnswerWrapper}>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.preAnswerButton}
|
||||
onClick={preAnswer(question5Ref)}
|
||||
>
|
||||
Je n’ai pas pu venir.
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.preAnswerButton}
|
||||
onClick={preAnswer(question5Ref)}
|
||||
>
|
||||
Je n’ai pas arrêté.
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.preAnswerButton}
|
||||
onClick={preAnswer(question5Ref)}
|
||||
>
|
||||
Je manquais d’infos.
|
||||
</button>
|
||||
</div>
|
||||
<textarea id="question5" ref={question5Ref} placeholder="..." />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={styles.questionWrapper}>
|
||||
<label htmlFor="question6">
|
||||
As-tu trouvé ton compte vis-à-vis des raisons qui t'ont poussées à être bénévole
|
||||
(faire découvrir sa passion, se rendre utile, découvrir l’envers du décor,
|
||||
sympathiser, apprendre, simplement kiffer, etc) ?
|
||||
</label>
|
||||
<div className={styles.preAnswerWrapper}>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.preAnswerButton}
|
||||
onClick={preAnswer(question6Ref)}
|
||||
>
|
||||
Oui, objectifs atteints.
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.preAnswerButton}
|
||||
onClick={preAnswer(question6Ref)}
|
||||
>
|
||||
Je n’avais pas d’attente.
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.preAnswerButton}
|
||||
onClick={preAnswer(question6Ref)}
|
||||
>
|
||||
Il m’a manqué __
|
||||
</button>
|
||||
</div>
|
||||
<textarea id="question6" ref={question6Ref} placeholder="..." />
|
||||
</div>
|
||||
|
||||
<div className={styles.questionWrapper}>
|
||||
<label htmlFor="question7">
|
||||
Si d’autres bénévoles sont partants pour te soutenir, que mettrais-tu en œuvre
|
||||
autrement ou mieux pour la prochaine édition du festival ?
|
||||
</label>
|
||||
<div className={styles.preAnswerWrapper}>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.preAnswerButton}
|
||||
onClick={preAnswer(question7Ref)}
|
||||
>
|
||||
Dans l’équipe __ j’aimerais __
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.preAnswerButton}
|
||||
onClick={preAnswer(question7Ref)}
|
||||
>
|
||||
B. La réponse B.
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.preAnswerButton}
|
||||
onClick={preAnswer(question7Ref)}
|
||||
>
|
||||
Je manque de temps.
|
||||
</button>
|
||||
</div>
|
||||
<textarea id="question7" ref={question7Ref} placeholder="..." />
|
||||
</div>
|
||||
|
||||
<div className={styles.questionWrapper}>
|
||||
<label htmlFor="question8">
|
||||
Envisages-tu d’être bénévole pour PeL 2023 ? Dans la même équipe, ou tu aurais
|
||||
envie d’en changer ? Libre à toi d’expliquer ou non pourquoi.
|
||||
</label>
|
||||
<div className={styles.preAnswerWrapper}>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.preAnswerButton}
|
||||
onClick={preAnswer(question8Ref)}
|
||||
>
|
||||
Oui, si je suis dispo.
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.preAnswerButton}
|
||||
onClick={preAnswer(question8Ref)}
|
||||
>
|
||||
Oui, mais dans l’équipe __
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.preAnswerButton}
|
||||
onClick={preAnswer(question8Ref)}
|
||||
>
|
||||
Hélas non.
|
||||
</button>
|
||||
</div>
|
||||
<textarea id="question8" ref={question8Ref} placeholder="..." />
|
||||
</div>
|
||||
|
||||
<div className={styles.questionWrapper}>
|
||||
<label htmlFor="question9">
|
||||
As-tu un conseil, une remarque, une idée non couverte par les précédentes
|
||||
questions ? Laisse ton imagination déborder ^^
|
||||
</label>
|
||||
<textarea id="question9" ref={question9Ref} placeholder="..." />
|
||||
</div>
|
||||
|
||||
<div className={styles.buttonWrapper}>
|
||||
<FormButton onClick={onSubmit}>Enregistrer</FormButton>
|
||||
{children === undefined && (
|
||||
<>
|
||||
{" "}
|
||||
<FormButton onClick={afterSubmit} type="grey">
|
||||
Annuler
|
||||
</FormButton>{" "}
|
||||
</>
|
||||
)}
|
||||
{children !== undefined && (
|
||||
<>
|
||||
{" "}
|
||||
<IgnoreButton onClick={afterSubmit} text="Ignorer pour l'instant">
|
||||
{children}
|
||||
</IgnoreButton>{" "}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
RetexForm.defaultProps = {
|
||||
children: undefined,
|
||||
afterSubmit: undefined,
|
||||
}
|
||||
|
||||
export default memo(RetexForm)
|
||||
|
||||
// Fetch server-side data here
|
||||
export const fetchFor = [fetchRetexSetIfNeed]
|
18
src/components/VolunteerBoard/RetexForm/RetexFormModal.tsx
Normal file
18
src/components/VolunteerBoard/RetexForm/RetexFormModal.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import { FC, memo, useCallback } from "react"
|
||||
import { hideModal, MODAL_IDS } from "../../../store/ui"
|
||||
import Modal from "../../Modal/Modal"
|
||||
import useAction from "../../../utils/useAction"
|
||||
import RetexForm from "./RetexForm"
|
||||
|
||||
const RetexFormModal: FC = (): JSX.Element => {
|
||||
const execHideModal = useAction(hideModal)
|
||||
const afterFormSubmit = useCallback(() => execHideModal(), [execHideModal])
|
||||
|
||||
return (
|
||||
<Modal modalId={MODAL_IDS.RETEX}>
|
||||
<RetexForm afterSubmit={afterFormSubmit} />
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(RetexFormModal)
|
109
src/components/VolunteerBoard/RetexForm/styles.module.scss
Executable file
109
src/components/VolunteerBoard/RetexForm/styles.module.scss
Executable file
@ -0,0 +1,109 @@
|
||||
@import "../../../theme/variables";
|
||||
@import "../../../theme/mixins";
|
||||
|
||||
.title {
|
||||
padding: 4px;
|
||||
margin: 15px 0;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.inputWrapper {
|
||||
margin: 25px 0;
|
||||
|
||||
@include desktop {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
min-width: 175px;
|
||||
border: 1px solid $color-grey-medium;
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.leftCol {
|
||||
flex: 0 0 240px;
|
||||
}
|
||||
|
||||
.rightCol {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.brunchTitle {
|
||||
display: inline-block;
|
||||
width: 320px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.invisible {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.brunchLabel {
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
width: 280px;
|
||||
}
|
||||
|
||||
.brunchPresenceTitle {
|
||||
display: inline-block;
|
||||
width: 320px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.brunchPresenceLabel {
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.guestCountTitle {
|
||||
display: inline-block;
|
||||
width: 320px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.guestCountLabel {
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.questionWrapper {
|
||||
margin: 6px 0 14px;
|
||||
|
||||
label {
|
||||
display: block;
|
||||
padding: 6px 0 2px 4px;
|
||||
}
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: 75px;
|
||||
padding: 5px;
|
||||
border: 1px solid $color-grey-light;
|
||||
background-color: $color-grey-lighter;
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.buttonWrapper {
|
||||
margin-bottom: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.preAnswerWrapper {
|
||||
margin-bottom: 3px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.preAnswerButton {
|
||||
@include form-button;
|
||||
|
||||
margin-left: 2px;
|
||||
margin-top: 2px;
|
||||
}
|
@ -110,7 +110,7 @@ const TeamWishesForm: FC<Props> = ({ children, afterSubmit }): JSX.Element | nul
|
||||
{children !== undefined && (
|
||||
<>
|
||||
{" "}
|
||||
<IgnoreButton onClick={afterSubmit} text="Ignorer">
|
||||
<IgnoreButton onClick={afterSubmit} text="Ignorer pour l'instant">
|
||||
{children}
|
||||
</IgnoreButton>{" "}
|
||||
</>
|
||||
|
25
src/components/VolunteerBoard/brunch.utils.ts
Normal file
25
src/components/VolunteerBoard/brunch.utils.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { useCallback } from "react"
|
||||
import { shallowEqual, useSelector } from "react-redux"
|
||||
import useAction from "../../utils/useAction"
|
||||
import { selectUserJwtToken } from "../../store/auth"
|
||||
import { AppState } from "../../store"
|
||||
import { fetchRetexSet } from "../../store/retexSet"
|
||||
import { Retex } from "../../services/retex"
|
||||
|
||||
type SetFunction = (id: Retex["id"], question1: Retex["question1"]) => void
|
||||
|
||||
export const useBrunch = (): [Retex | undefined, SetFunction] => {
|
||||
const save = useAction(fetchRetexSet)
|
||||
const jwtToken = useSelector(selectUserJwtToken)
|
||||
const retex = useSelector((state: AppState) => state.retexSet?.entity, shallowEqual)
|
||||
|
||||
const saveBrunch: SetFunction = useCallback(
|
||||
(id, question1) => {
|
||||
if (!retex) return
|
||||
save(jwtToken, { id, question1 })
|
||||
},
|
||||
[retex, save, jwtToken]
|
||||
)
|
||||
|
||||
return [retex, saveBrunch]
|
||||
}
|
55
src/components/VolunteerBoard/retex.utils.ts
Normal file
55
src/components/VolunteerBoard/retex.utils.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { useCallback } from "react"
|
||||
import { shallowEqual, useSelector } from "react-redux"
|
||||
import useAction from "../../utils/useAction"
|
||||
import { selectUserJwtToken } from "../../store/auth"
|
||||
import { AppState } from "../../store"
|
||||
import { fetchRetexSet } from "../../store/retexSet"
|
||||
import { Retex } from "../../services/retex"
|
||||
|
||||
type SetFunction = (
|
||||
id: Retex["id"],
|
||||
question2: Retex["question2"],
|
||||
question3: Retex["question3"],
|
||||
question4: Retex["question4"],
|
||||
question5: Retex["question5"],
|
||||
question6: Retex["question6"],
|
||||
question7: Retex["question7"],
|
||||
question8: Retex["question8"],
|
||||
question9: Retex["question9"]
|
||||
) => void
|
||||
|
||||
export const useRetex = (): [Retex | undefined, SetFunction] => {
|
||||
const save = useAction(fetchRetexSet)
|
||||
const jwtToken = useSelector(selectUserJwtToken)
|
||||
const retex = useSelector((state: AppState) => state.retexSet?.entity, shallowEqual)
|
||||
|
||||
const saveRetex: SetFunction = useCallback(
|
||||
(
|
||||
id,
|
||||
question2,
|
||||
question3,
|
||||
question4,
|
||||
question5,
|
||||
question6,
|
||||
question7,
|
||||
question8,
|
||||
question9
|
||||
) => {
|
||||
if (!retex) return
|
||||
save(jwtToken, {
|
||||
id,
|
||||
question2,
|
||||
question3,
|
||||
question4,
|
||||
question5,
|
||||
question6,
|
||||
question7,
|
||||
question8,
|
||||
question9,
|
||||
})
|
||||
},
|
||||
[retex, save, jwtToken]
|
||||
)
|
||||
|
||||
return [retex, saveRetex]
|
||||
}
|
@ -20,6 +20,8 @@ export class SheetNames {
|
||||
|
||||
Postulants = "Postulants"
|
||||
|
||||
Retex = "Retex"
|
||||
|
||||
Teams = "Equipes"
|
||||
|
||||
Volunteers = "Membres"
|
||||
|
59
src/server/gsheets/retex.ts
Normal file
59
src/server/gsheets/retex.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import { cloneDeep } from "lodash"
|
||||
|
||||
import ExpressAccessors from "./expressAccessors"
|
||||
import { Retex, RetexWithoutId, translationRetex } from "../../services/retex"
|
||||
|
||||
const expressAccessor = new ExpressAccessors<RetexWithoutId, Retex>(
|
||||
"Retex",
|
||||
new Retex(),
|
||||
translationRetex
|
||||
)
|
||||
|
||||
export const retexSet = expressAccessor.set(async (list, body, id, _roles) => {
|
||||
const receivedRetex = body[0] as Record<keyof Retex, string>
|
||||
if (id !== +receivedRetex.id) {
|
||||
throw Error(`Retex modifié pour un autre member que soit`)
|
||||
}
|
||||
const retex: Retex | undefined = list.find((v) => v.id === +receivedRetex.id)
|
||||
if (!retex) {
|
||||
throw Error(`Il n'y a aucun bénévole avec cet identifiant ${receivedRetex.id}`)
|
||||
}
|
||||
const newRetex: Retex = cloneDeep(retex)
|
||||
|
||||
const parsedPartialRetex = expressAccessor.parseRawPartialElement(receivedRetex)
|
||||
if (parsedPartialRetex === undefined) {
|
||||
throw Error(`Erreur au parsing dans retexSet`)
|
||||
}
|
||||
|
||||
if (parsedPartialRetex.question1 !== undefined)
|
||||
newRetex.question1 = parsedPartialRetex.question1
|
||||
|
||||
if (parsedPartialRetex.question2 !== undefined)
|
||||
newRetex.question2 = parsedPartialRetex.question2
|
||||
|
||||
if (parsedPartialRetex.question3 !== undefined)
|
||||
newRetex.question3 = parsedPartialRetex.question3
|
||||
|
||||
if (parsedPartialRetex.question4 !== undefined)
|
||||
newRetex.question4 = parsedPartialRetex.question4
|
||||
|
||||
if (parsedPartialRetex.question5 !== undefined)
|
||||
newRetex.question5 = parsedPartialRetex.question5
|
||||
|
||||
if (parsedPartialRetex.question6 !== undefined)
|
||||
newRetex.question6 = parsedPartialRetex.question6
|
||||
|
||||
if (parsedPartialRetex.question7 !== undefined)
|
||||
newRetex.question7 = parsedPartialRetex.question7
|
||||
|
||||
if (parsedPartialRetex.question8 !== undefined)
|
||||
newRetex.question8 = parsedPartialRetex.question8
|
||||
|
||||
if (parsedPartialRetex.question9 !== undefined)
|
||||
newRetex.question9 = parsedPartialRetex.question9
|
||||
|
||||
return {
|
||||
toDatabase: newRetex,
|
||||
toCaller: newRetex,
|
||||
}
|
||||
})
|
@ -47,6 +47,7 @@ import checkAccess from "./checkAccess"
|
||||
import { hasGSheetsAccess } from "./gsheets/accessors"
|
||||
import { addStatus, showStatusAt } from "./status"
|
||||
import { miscMeetingDateListGet, miscDiscordInvitation } from "./gsheets/miscs"
|
||||
import { retexSet } from "./gsheets/retex"
|
||||
|
||||
checkAccess()
|
||||
|
||||
@ -108,6 +109,7 @@ app.get("/VolunteerListGet", secure as RequestHandler, volunteerListGet)
|
||||
// Secured APIs
|
||||
app.get("/AnnouncementListGet", secure as RequestHandler, announcementListGet)
|
||||
app.get("/MiscDiscordInvitationGet", secure as RequestHandler, miscDiscordInvitation)
|
||||
app.post("/RetexSet", secure as RequestHandler, retexSet)
|
||||
app.get("/TeamListGet", teamListGet)
|
||||
app.get("/VolunteerDiscordId", secure as RequestHandler, volunteerDiscordId)
|
||||
app.post("/VolunteerAsksSet", secure as RequestHandler, volunteerAsksSet)
|
||||
|
56
src/services/retex.ts
Normal file
56
src/services/retex.ts
Normal file
@ -0,0 +1,56 @@
|
||||
/* eslint-disable max-classes-per-file */
|
||||
export class Retex {
|
||||
id = 0
|
||||
|
||||
dayWishes = ""
|
||||
|
||||
question1 = -1
|
||||
|
||||
question2 = ""
|
||||
|
||||
question3 = ""
|
||||
|
||||
question4 = ""
|
||||
|
||||
question5 = ""
|
||||
|
||||
question6 = ""
|
||||
|
||||
question7 = ""
|
||||
|
||||
question8 = ""
|
||||
|
||||
question9 = ""
|
||||
}
|
||||
|
||||
export const translationRetex: { [k in keyof Retex]: string } = {
|
||||
id: "id",
|
||||
dayWishes: "enviesJours",
|
||||
question1: "question1",
|
||||
question2: "question2",
|
||||
question3: "question3",
|
||||
question4: "question4",
|
||||
question5: "question5",
|
||||
question6: "question6",
|
||||
question7: "question7",
|
||||
question8: "question8",
|
||||
question9: "question9",
|
||||
}
|
||||
|
||||
export const elementName = "Retex"
|
||||
|
||||
export const volunteerExample: Retex = {
|
||||
id: 1,
|
||||
dayWishes: "J,S,D",
|
||||
question1: 1,
|
||||
question2: "Amélie",
|
||||
question3: "sadasdsa",
|
||||
question4: "",
|
||||
question5: "sadasdsa",
|
||||
question6: "",
|
||||
question7: "",
|
||||
question8: "sadasdsa",
|
||||
question9: "sadasdsa",
|
||||
}
|
||||
|
||||
export type RetexWithoutId = Omit<Retex, "id">
|
6
src/services/retexAccessors.ts
Normal file
6
src/services/retexAccessors.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import ServiceAccessors from "./accessors"
|
||||
import { elementName, Retex, RetexWithoutId } from "./retex"
|
||||
|
||||
const serviceAccessors = new ServiceAccessors<RetexWithoutId, Retex>(elementName)
|
||||
|
||||
export const retexSet = serviceAccessors.securedCustomPost<[Partial<Retex>]>("Set")
|
53
src/store/retexSet.ts
Normal file
53
src/store/retexSet.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { PayloadAction, createSlice, createSelector } from "@reduxjs/toolkit"
|
||||
|
||||
import { StateRequest, toastError, elementFetch } from "./utils"
|
||||
import { Retex } from "../services/retex"
|
||||
import { retexSet } from "../services/retexAccessors"
|
||||
import { AppState, AppThunk } from "."
|
||||
|
||||
type StateRetex = { entity?: Retex } & StateRequest
|
||||
|
||||
export const initialState: StateRetex = {
|
||||
readyStatus: "idle",
|
||||
}
|
||||
|
||||
const retexSetSlice = createSlice({
|
||||
name: "retexSet",
|
||||
initialState,
|
||||
reducers: {
|
||||
getRequesting: (_) => ({
|
||||
readyStatus: "request",
|
||||
}),
|
||||
getSuccess: (_, { payload }: PayloadAction<Retex>) => ({
|
||||
readyStatus: "success",
|
||||
entity: payload,
|
||||
}),
|
||||
getFailure: (_, { payload }: PayloadAction<string>) => ({
|
||||
readyStatus: "failure",
|
||||
error: payload,
|
||||
}),
|
||||
},
|
||||
})
|
||||
|
||||
export default retexSetSlice.reducer
|
||||
export const { getRequesting, getSuccess, getFailure } = retexSetSlice.actions
|
||||
|
||||
export const fetchRetexSet = elementFetch(
|
||||
retexSet,
|
||||
getRequesting,
|
||||
getSuccess,
|
||||
getFailure,
|
||||
(error: Error) => toastError(`Erreur lors de la modification d'un retex: ${error.message}`)
|
||||
)
|
||||
|
||||
export const fetchRetexSetIfNeed =
|
||||
(newPartialRetex?: Partial<Retex>): AppThunk =>
|
||||
(dispatch, getState) => {
|
||||
const { jwt, id } = getState().auth
|
||||
return dispatch(fetchRetexSet(jwt, newPartialRetex || { id }))
|
||||
}
|
||||
|
||||
export const selectRetexSet = createSelector(
|
||||
(state: AppState) => state,
|
||||
(state): Retex | undefined => state.retexSet?.entity
|
||||
)
|
@ -9,6 +9,7 @@ import gameDetailsUpdate from "./gameDetailsUpdate"
|
||||
import miscDiscordInvitation from "./miscDiscordInvitation"
|
||||
import miscMeetingDateList from "./miscMeetingDateList"
|
||||
import postulantAdd from "./postulantAdd"
|
||||
import retexSet from "./retexSet"
|
||||
import teamList from "./teamList"
|
||||
import ui from "./ui"
|
||||
import volunteerPartialAdd from "./volunteerPartialAdd"
|
||||
@ -41,6 +42,7 @@ export default (history: History) => ({
|
||||
miscDiscordInvitation,
|
||||
miscMeetingDateList,
|
||||
postulantAdd,
|
||||
retexSet,
|
||||
teamList,
|
||||
ui,
|
||||
volunteerPartialAdd,
|
||||
|
@ -27,10 +27,12 @@ const selectUiData = (state: AppState) => state.ui
|
||||
export const selectActiveModalId = createSelector(selectUiData, (ui) => ui.modalId)
|
||||
|
||||
export const MODAL_IDS = {
|
||||
BRUNCH: "BRUNCH",
|
||||
DAYWISHES: "DAYWISHES",
|
||||
HOSTING: "HOSTING",
|
||||
MEALS: "MEALS",
|
||||
PARTICIPATIONDETAILS: "PARTICIPATIONDETAILS",
|
||||
PERSONALINFO: "PERSONALINFO",
|
||||
RETEX: "RETEX",
|
||||
TEAMWISHES: "TEAMWISHES",
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user