mirror of
https://github.com/Paris-est-Ludique/intranet.git
synced 2025-06-08 08:34:20 +02:00
575 lines
22 KiB
TypeScript
575 lines
22 KiB
TypeScript
import { FC, memo, ReactNode, useCallback, useEffect, useRef, useState } from "react"
|
||
import classnames from "classnames"
|
||
import get from "lodash/get"
|
||
import includes from "lodash/includes"
|
||
import without from "lodash/without"
|
||
import set from "lodash/set"
|
||
import styles from "./styles.module.scss"
|
||
import {
|
||
useUserHosting,
|
||
nightList,
|
||
NightOption,
|
||
nightChoiceSelectionDefaultState,
|
||
NightChoices,
|
||
bedList,
|
||
hostLocations,
|
||
} from "../hosting.utils"
|
||
import FormButton from "../../Form/FormButton/FormButton"
|
||
import { fetchVolunteerHostingSetIfNeed } from "../../../store/volunteerHostingSet"
|
||
import IgnoreButton from "../../Form/IgnoreButton/IgnoreButton"
|
||
|
||
type Props = {
|
||
children?: ReactNode | undefined
|
||
afterSubmit?: () => void | undefined
|
||
}
|
||
|
||
const HostingForm: FC<Props> = ({ children, afterSubmit }): JSX.Element => {
|
||
const [hostingType, setHostingType] = useState("")
|
||
const canHostCountRef = useRef<HTMLInputElement | null>(null)
|
||
const [nights, setNights] = useState(nightChoiceSelectionDefaultState)
|
||
const cohostVolunteerRef = useRef<HTMLInputElement | null>(null)
|
||
const petAllergiesRef = useRef<HTMLInputElement | null>(null)
|
||
const [backProblems, setBackProblems] = useState(false)
|
||
const [bedType, setBedType] = useState<string[]>([])
|
||
const [isolatedBed, setIsolatedBed] = useState(false)
|
||
const bedConfigurationRef = useRef<HTMLTextAreaElement | null>(null)
|
||
const hostAddressRef = useRef<HTMLTextAreaElement | null>(null)
|
||
const transportTypeRef = useRef<HTMLInputElement | null>(null)
|
||
const [festivalProximity, setFestivalProximity] = useState("")
|
||
const distanceToFestivalRef = useRef<HTMLInputElement | null>(null)
|
||
const [hostingAbsoluteNeed, setHostingAbsoluteNeed] = useState(false)
|
||
const hostingNeedReasonRef = useRef<HTMLTextAreaElement | null>(null)
|
||
const [userWishes, saveWishes] = useUserHosting()
|
||
|
||
useEffect(() => {
|
||
if (!userWishes) return
|
||
setHostingType(get(userWishes, "hostingType", ""))
|
||
const hostingNights = get(userWishes, "hostingNights", "") as string
|
||
const newNights = hostingNights.split("").reduce(
|
||
(acc: NightChoices, abbr: string) => ({
|
||
...acc,
|
||
[abbr]: true,
|
||
}),
|
||
{} as NightChoices
|
||
)
|
||
setNights(newNights)
|
||
setBedType(get(userWishes, "bedType", []))
|
||
setBackProblems(get(userWishes, "backProblems", false))
|
||
set(canHostCountRef, "current.value", `${get(userWishes, "canHostCount", 0)}`)
|
||
set(cohostVolunteerRef, "current.value", get(userWishes, "cohostVolunteer", ""))
|
||
set(petAllergiesRef, "current.value", get(userWishes, "petAllergies", ""))
|
||
set(bedConfigurationRef, "current.value", get(userWishes, "bedConfiguration", ""))
|
||
set(hostAddressRef, "current.value", get(userWishes, "hostAddress", ""))
|
||
set(transportTypeRef, "current.value", get(userWishes, "transportType", ""))
|
||
setFestivalProximity(get(userWishes, "festivalProximity", ""))
|
||
set(distanceToFestivalRef, "current.value", get(userWishes, "distanceToFestival", ""))
|
||
set(hostingNeedReasonRef, "current.value", get(userWishes, "hostingNeedReason", ""))
|
||
setHostingAbsoluteNeed(get(userWishes, "hostingAbsoluteNeed", false))
|
||
}, [userWishes])
|
||
|
||
const onHostingTypeChange = useCallback((newHostingType: string) => {
|
||
setHostingType(newHostingType)
|
||
}, [])
|
||
|
||
const onBackProblemsChange = useCallback((newBackProblems: boolean) => {
|
||
setBackProblems(newBackProblems)
|
||
}, [])
|
||
|
||
const onNightClick = useCallback(
|
||
(changes: NightChoices) => {
|
||
setNights({
|
||
...nights,
|
||
...changes,
|
||
})
|
||
},
|
||
[nights, setNights]
|
||
)
|
||
|
||
function isNightUnselected(abbr: string): boolean {
|
||
return !nights[abbr] && !nights[abbr.toLowerCase()]
|
||
}
|
||
function isNightSelected(abbr: string): boolean {
|
||
return nights[abbr]
|
||
}
|
||
|
||
const onBedTypeChange = useCallback(
|
||
(bed: string) => {
|
||
if (includes(bedType, bed)) {
|
||
setBedType(without(bedType, bed))
|
||
} else {
|
||
setBedType([...bedType, bed])
|
||
}
|
||
},
|
||
[bedType]
|
||
)
|
||
|
||
const onIsolatedBedChange = useCallback((newIsolatedBed: boolean) => {
|
||
setIsolatedBed(newIsolatedBed)
|
||
}, [])
|
||
|
||
const onHostingAbsoluteNeedChange = useCallback((newHostingAbsoluteNeed: boolean) => {
|
||
setHostingAbsoluteNeed(newHostingAbsoluteNeed)
|
||
}, [])
|
||
|
||
const onFestivalProximityChange = useCallback((newFestivalProximity: string) => {
|
||
setFestivalProximity(newFestivalProximity)
|
||
}, [])
|
||
|
||
const onChoiceSubmit = useCallback(() => {
|
||
const canHostCount = +get(canHostCountRef, "current.value", "0")
|
||
const cohostVolunteer = get(cohostVolunteerRef, "current.value", "")
|
||
const bedConfiguration = get(bedConfigurationRef, "current.value", "")
|
||
const hostingNights = nightList.reduce((res, { abbr }) => {
|
||
if (nights[abbr]) {
|
||
res += abbr
|
||
}
|
||
if (nights[abbr.toLowerCase()]) {
|
||
res += abbr.toLowerCase()
|
||
}
|
||
return res
|
||
}, "")
|
||
const hostAddress = get(hostAddressRef, "current.value", "")
|
||
const petAllergies = get(petAllergiesRef, "current.value", "")
|
||
const transportType = get(transportTypeRef, "current.value", "")
|
||
const distanceToFestival = get(distanceToFestivalRef, "current.value", "")
|
||
const hostingNeedReason = get(hostingNeedReasonRef, "current.value", "")
|
||
saveWishes(
|
||
hostingType,
|
||
canHostCount,
|
||
cohostVolunteer,
|
||
backProblems,
|
||
hostingNights,
|
||
bedType,
|
||
isolatedBed,
|
||
bedConfiguration,
|
||
hostAddress,
|
||
petAllergies,
|
||
transportType,
|
||
festivalProximity,
|
||
distanceToFestival,
|
||
hostingNeedReason,
|
||
hostingAbsoluteNeed
|
||
)
|
||
if (afterSubmit) afterSubmit()
|
||
}, [
|
||
saveWishes,
|
||
hostingType,
|
||
backProblems,
|
||
bedType,
|
||
isolatedBed,
|
||
festivalProximity,
|
||
hostingAbsoluteNeed,
|
||
afterSubmit,
|
||
nights,
|
||
])
|
||
|
||
const getNightElement = (option: NightOption, maybeText: string): JSX.Element => (
|
||
<div className={classnames(styles.inputWrapper, styles.noBottomMargin)} key={option.abbr}>
|
||
<div className={styles.leftCol}>
|
||
<div className={styles.nightTitle}>{option.title}</div>
|
||
</div>
|
||
<div className={styles.rightCol}>
|
||
<label className={styles.nightLabel} key={`no${option.abbr}`}>
|
||
<input
|
||
type="radio"
|
||
name={`no${option.abbr}`}
|
||
onChange={() =>
|
||
onNightClick({
|
||
[option.abbr]: false,
|
||
[option.abbr.toLowerCase()]: false,
|
||
})
|
||
}
|
||
checked={isNightUnselected(option.abbr)}
|
||
/>{" "}
|
||
Non
|
||
</label>
|
||
<label className={styles.nightLabelLong} key={option.abbr.toLowerCase()}>
|
||
<input
|
||
type="radio"
|
||
name={option.abbr.toLowerCase()}
|
||
onChange={() =>
|
||
onNightClick({
|
||
[option.abbr]: false,
|
||
[option.abbr.toLowerCase()]: true,
|
||
})
|
||
}
|
||
checked={isNightSelected(option.abbr.toLowerCase())}
|
||
/>
|
||
{maybeText}
|
||
</label>
|
||
<label className={styles.nightLabel} key={option.abbr}>
|
||
<input
|
||
type="radio"
|
||
name={option.abbr}
|
||
onChange={() =>
|
||
onNightClick({
|
||
[option.abbr]: true,
|
||
[option.abbr.toLowerCase()]: false,
|
||
})
|
||
}
|
||
checked={isNightSelected(option.abbr)}
|
||
/>{" "}
|
||
Oui
|
||
</label>
|
||
</div>
|
||
</div>
|
||
)
|
||
|
||
return (
|
||
<div>
|
||
<div className={styles.title}>Mon hébergement</div>
|
||
|
||
<div
|
||
className={classnames(
|
||
styles.inputWrapper,
|
||
styles.noBottomMargin,
|
||
styles.hostingTypeForm
|
||
)}
|
||
>
|
||
<div className={styles.rightCol}>
|
||
<label className={styles.hostingTypeLabel}>
|
||
<input
|
||
type="radio"
|
||
name="can"
|
||
onChange={() => onHostingTypeChange("can")}
|
||
checked={hostingType === "can"}
|
||
/>{" "}
|
||
Je peux héberger au moins un bénévole
|
||
</label>
|
||
<label className={styles.hostingTypeLabel}>
|
||
<input
|
||
type="radio"
|
||
name="need"
|
||
onChange={() => onHostingTypeChange("need")}
|
||
checked={hostingType === "need"}
|
||
/>{" "}
|
||
J'aurais besoin d'un hébergement de la part d'un bénévole proche du festival
|
||
</label>
|
||
<label className={styles.hostingTypeLabel}>
|
||
<input
|
||
type="radio"
|
||
name="neither"
|
||
onChange={() => onHostingTypeChange("")}
|
||
checked={hostingType === ""}
|
||
/>{" "}
|
||
Aucun des deux
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
className={classnames([styles.inputWrapper, hostingType !== "can" && styles.hide])}
|
||
>
|
||
<div className={styles.leftCol}>
|
||
<div className={styles.canHostCountTitle}>
|
||
Combien de bénévoles peux-tu héberger ?
|
||
</div>
|
||
</div>
|
||
<div className={styles.rightCol}>
|
||
<input className={styles.canHostCountLabel} type="text" ref={canHostCountRef} />
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
className={classnames([styles.inputWrapper, hostingType !== "can" && styles.hide])}
|
||
>
|
||
<div className={styles.leftCol}>
|
||
<div className={styles.petAllergiesTitle}>
|
||
As-tu un animal de compagnie ? (Possibles allergies ou phobies) ?
|
||
</div>
|
||
</div>
|
||
<div className={styles.rightCol}>
|
||
<input className={styles.petAllergiesLabel} type="text" ref={petAllergiesRef} />
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
className={classnames([
|
||
styles.inputWrapper,
|
||
styles.noBottomMargin,
|
||
hostingType !== "need" && styles.hide,
|
||
])}
|
||
>
|
||
<div className={styles.leftCol}>
|
||
<div className={styles.backProblemsTitle}>As-tu des problèmes de dos ?</div>
|
||
</div>
|
||
<div className={styles.rightCol}>
|
||
<label className={styles.backProblemsLabel}>
|
||
<input
|
||
type="radio"
|
||
name="backProblems"
|
||
onChange={() => onBackProblemsChange(true)}
|
||
checked={backProblems}
|
||
/>{" "}
|
||
Oui
|
||
</label>
|
||
<label className={styles.backProblemsLabel}>
|
||
<input
|
||
type="radio"
|
||
name="backProblems"
|
||
onChange={() => onBackProblemsChange(false)}
|
||
checked={!backProblems}
|
||
/>{" "}
|
||
Non
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
className={classnames([
|
||
hostingType === "" || hostingType === "neither" ? styles.hide : null,
|
||
])}
|
||
>
|
||
{nightList.map((nightOption) =>
|
||
getNightElement(
|
||
nightOption,
|
||
hostingType === "need"
|
||
? " J'aimerai bien mais pas obligatoire"
|
||
: " Je peux mais ça ne m'arrange pas trop"
|
||
)
|
||
)}
|
||
</div>
|
||
|
||
<div
|
||
className={classnames([
|
||
styles.inputWrapper,
|
||
styles.noBottomMargin,
|
||
hostingType !== "can" && styles.hide,
|
||
])}
|
||
>
|
||
<div className={styles.leftCol}>
|
||
<div className={styles.bedTypeTitle}>Le couchage que tu proposes est-il :</div>
|
||
</div>
|
||
<div className={styles.rightCol}>
|
||
{bedList.map((bed) => (
|
||
<label className={styles.bedTypeLabel} key={bed}>
|
||
<input
|
||
type="checkbox"
|
||
value="oui"
|
||
name={bed}
|
||
onChange={() => onBedTypeChange(bed)}
|
||
checked={includes(bedType, bed)}
|
||
/>{" "}
|
||
{bed}
|
||
</label>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
className={classnames([
|
||
styles.inputWrapper,
|
||
styles.noBottomMargin,
|
||
hostingType !== "can" && styles.hide,
|
||
])}
|
||
>
|
||
<div className={styles.leftCol}>
|
||
<div className={styles.isolatedBedTitle}>
|
||
Le couchage est-il dans une pièce séparée ?
|
||
</div>
|
||
</div>
|
||
<div className={styles.rightCol}>
|
||
<label className={styles.isolatedBedLabel}>
|
||
<input
|
||
type="radio"
|
||
name="isolatedBed"
|
||
onChange={() => onIsolatedBedChange(true)}
|
||
checked={isolatedBed}
|
||
/>{" "}
|
||
Oui
|
||
</label>
|
||
<label className={styles.isolatedBedLabel}>
|
||
<input
|
||
type="radio"
|
||
name="isolatedBed"
|
||
onChange={() => onIsolatedBedChange(false)}
|
||
checked={!isolatedBed}
|
||
/>{" "}
|
||
Non
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
className={classnames([
|
||
styles.inputWrapper,
|
||
styles.bedConfigurationWrapper,
|
||
hostingType !== "can" && styles.hide,
|
||
])}
|
||
>
|
||
<div className={styles.leftCol}>
|
||
<div className={styles.bedConfigurationTitle}>
|
||
Si tu peux héberger plusieurs bénévoles, quelle est la configuration des
|
||
couchages proposés ?
|
||
</div>
|
||
</div>
|
||
<div className={styles.rightCol}>
|
||
<textarea id="bedConfiguration-comment" ref={bedConfigurationRef} />
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
className={classnames([
|
||
styles.inputWrapper,
|
||
styles.hostAddressWrapper,
|
||
hostingType !== "can" && styles.hide,
|
||
])}
|
||
>
|
||
<div className={styles.leftCol}>
|
||
<div className={styles.hostAddressTitle}>
|
||
Où habites-tu ? Au minium, quelle ville ?
|
||
</div>
|
||
</div>
|
||
<div className={styles.rightCol}>
|
||
<textarea id="hostAddress" ref={hostAddressRef} />
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
className={classnames([
|
||
styles.inputWrapper,
|
||
styles.noBottomMargin,
|
||
hostingType !== "need" && styles.hide,
|
||
])}
|
||
>
|
||
<div className={styles.leftCol}>
|
||
<div className={styles.festivalProximityTitle}>Où habites-tu ?</div>
|
||
</div>
|
||
<div className={styles.rightCol}>
|
||
{hostLocations.map((hostLocation) => (
|
||
<label className={styles.festivalProximityLabel} key={hostLocation}>
|
||
<input
|
||
type="radio"
|
||
name="festivalProximity"
|
||
onChange={() => onFestivalProximityChange(hostLocation)}
|
||
checked={hostLocation === festivalProximity}
|
||
/>{" "}
|
||
{hostLocation}
|
||
</label>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
className={classnames([styles.inputWrapper, hostingType !== "can" && styles.hide])}
|
||
>
|
||
<div className={styles.leftCol}>
|
||
<div className={styles.transportTypeTitle}>
|
||
Par quel moyen de transport prévois-tu de venir avec le/les bénévole(s) ?
|
||
</div>
|
||
</div>
|
||
<div className={styles.rightCol}>
|
||
<input
|
||
className={styles.transportTypeLabel}
|
||
type="text"
|
||
ref={transportTypeRef}
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
className={classnames([
|
||
styles.inputWrapper,
|
||
(hostingType === "" ||
|
||
hostingType === "neither" ||
|
||
(hostingType === "need" &&
|
||
(festivalProximity === "hors région parisienne" ||
|
||
festivalProximity === ""))) &&
|
||
styles.hide,
|
||
])}
|
||
>
|
||
<div className={styles.leftCol}>
|
||
<div className={styles.distanceToFestivalTitle}>
|
||
A combien de temps habites-tu de la pelouse de Reuilly porte à porte ?
|
||
</div>
|
||
</div>
|
||
<div className={styles.rightCol}>
|
||
<input
|
||
className={styles.distanceToFestivalLabel}
|
||
type="text"
|
||
ref={distanceToFestivalRef}
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
className={classnames([
|
||
styles.hostingNeedReasonWrapper,
|
||
(hostingType !== "need" ||
|
||
festivalProximity === "hors région parisienne" ||
|
||
festivalProximity === "") &&
|
||
styles.hide,
|
||
])}
|
||
>
|
||
<label htmlFor="hostingNeedReason">Pourquoi as-tu besoin d'un hébergement ?</label>
|
||
<textarea id="hostingNeedReason" ref={hostingNeedReasonRef} />
|
||
</div>
|
||
|
||
<div
|
||
className={classnames([
|
||
styles.inputWrapper,
|
||
styles.noBottomMargin,
|
||
hostingType !== "need" && styles.hide,
|
||
])}
|
||
>
|
||
<div className={styles.leftCol}>
|
||
<div className={styles.hostingAbsoluteNeedTitle}>
|
||
Pourras-tu venir au festival même si on ne te trouve pas d’hébergement
|
||
bénévole ?
|
||
</div>
|
||
</div>
|
||
<div className={styles.rightCol}>
|
||
<label className={styles.hostingAbsoluteNeedLabel}>
|
||
<input
|
||
type="radio"
|
||
name="hostingAbsoluteNeed"
|
||
onChange={() => onHostingAbsoluteNeedChange(true)}
|
||
checked={hostingAbsoluteNeed}
|
||
/>{" "}
|
||
Oui
|
||
</label>
|
||
<label className={styles.hostingAbsoluteNeedLabel}>
|
||
<input
|
||
type="radio"
|
||
name="hostingAbsoluteNeed"
|
||
onChange={() => onHostingAbsoluteNeedChange(false)}
|
||
checked={!hostingAbsoluteNeed}
|
||
/>{" "}
|
||
Non
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<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 pour l'instant">
|
||
{children}
|
||
</IgnoreButton>{" "}
|
||
</>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
HostingForm.defaultProps = {
|
||
children: undefined,
|
||
afterSubmit: undefined,
|
||
}
|
||
|
||
export default memo(HostingForm)
|
||
|
||
// Fetch server-side data here
|
||
export const fetchFor = [fetchVolunteerHostingSetIfNeed]
|