575 lines
22 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 dhé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]