mirror of
https://github.com/Paris-est-Ludique/intranet.git
synced 2025-06-09 09:04:20 +02:00
Add hosting form
This commit is contained in:
parent
f9a68d7cfe
commit
032e15d985
39
src/components/Asks/AskHosting.tsx
Normal file
39
src/components/Asks/AskHosting.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 ParticipationDetailsForm, {
|
||||||
|
fetchFor as fetchForParticipationDetailsForm,
|
||||||
|
} from "../VolunteerBoard/ParticipationDetailsForm/ParticipationDetailsForm"
|
||||||
|
import { useUserParticipationDetails } from "../VolunteerBoard/participationDetails.utils"
|
||||||
|
|
||||||
|
export function AskParticipationDetails(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 [participationDetails] = useUserParticipationDetails()
|
||||||
|
const tshirtSize = get(participationDetails, "tshirtSize", "")
|
||||||
|
const food = get(participationDetails, "food", "")
|
||||||
|
const needToShow = !tshirtSize || !food
|
||||||
|
|
||||||
|
addAsk(
|
||||||
|
asks,
|
||||||
|
id,
|
||||||
|
volunteerAsks,
|
||||||
|
false,
|
||||||
|
needToShow,
|
||||||
|
<ParticipationDetailsForm afterSubmit={onSubmit}>
|
||||||
|
{answerLaterOnProfile}
|
||||||
|
</ParticipationDetailsForm>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch server-side data here
|
||||||
|
export const fetchFor = [...fetchForParticipationDetailsForm]
|
@ -1,6 +1,8 @@
|
|||||||
import { FC, memo } from "react"
|
import { FC, memo } from "react"
|
||||||
import DayWishes from "./DayWishes/DayWishes"
|
import DayWishes from "./DayWishes/DayWishes"
|
||||||
import DayWishesFormModal from "./DayWishesForm/DayWishesFormModal"
|
import DayWishesFormModal from "./DayWishesForm/DayWishesFormModal"
|
||||||
|
import Hosting from "./Hosting/Hosting"
|
||||||
|
import HostingFormModal from "./HostingForm/HostingFormModal"
|
||||||
import ParticipationDetails from "./ParticipationDetails/ParticipationDetails"
|
import ParticipationDetails from "./ParticipationDetails/ParticipationDetails"
|
||||||
import ParticipationDetailsFormModal from "./ParticipationDetailsForm/ParticipationDetailsFormModal"
|
import ParticipationDetailsFormModal from "./ParticipationDetailsForm/ParticipationDetailsFormModal"
|
||||||
import TeamWishes from "./TeamWishes/TeamWishes"
|
import TeamWishes from "./TeamWishes/TeamWishes"
|
||||||
@ -8,6 +10,7 @@ import TeamWishesFormModal from "./TeamWishesForm/TeamWishesFormModal"
|
|||||||
import withUserConnected from "../../utils/withUserConnected"
|
import withUserConnected from "../../utils/withUserConnected"
|
||||||
import ContentTitle from "../ui/Content/ContentTitle"
|
import ContentTitle from "../ui/Content/ContentTitle"
|
||||||
import { fetchFor as fetchForDayWishesForm } from "./DayWishesForm/DayWishesForm"
|
import { fetchFor as fetchForDayWishesForm } from "./DayWishesForm/DayWishesForm"
|
||||||
|
import { fetchFor as fetchForHostingForm } from "./HostingForm/HostingForm"
|
||||||
import { fetchFor as fetchForParticipationDetailsForm } from "./ParticipationDetailsForm/ParticipationDetailsForm"
|
import { fetchFor as fetchForParticipationDetailsForm } from "./ParticipationDetailsForm/ParticipationDetailsForm"
|
||||||
import { fetchFor as fetchForTeamWishesForm } from "./TeamWishesForm/TeamWishesForm"
|
import { fetchFor as fetchForTeamWishesForm } from "./TeamWishesForm/TeamWishesForm"
|
||||||
import VolunteerTeam from "./VolunteerTeam/VolunteerTeam"
|
import VolunteerTeam from "./VolunteerTeam/VolunteerTeam"
|
||||||
@ -22,6 +25,8 @@ const Board: FC = (): JSX.Element => (
|
|||||||
<TeamWishes />
|
<TeamWishes />
|
||||||
<TeamWishesFormModal />
|
<TeamWishesFormModal />
|
||||||
<VolunteerTeam />
|
<VolunteerTeam />
|
||||||
|
<Hosting />
|
||||||
|
<HostingFormModal />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -29,6 +34,7 @@ export default memo(withUserConnected(Board))
|
|||||||
|
|
||||||
export const fetchFor = [
|
export const fetchFor = [
|
||||||
...fetchForDayWishesForm,
|
...fetchForDayWishesForm,
|
||||||
|
...fetchForHostingForm,
|
||||||
...fetchForParticipationDetailsForm,
|
...fetchForParticipationDetailsForm,
|
||||||
...fetchForTeamWishesForm,
|
...fetchForTeamWishesForm,
|
||||||
]
|
]
|
||||||
|
61
src/components/VolunteerBoard/Hosting/Hosting.tsx
Normal file
61
src/components/VolunteerBoard/Hosting/Hosting.tsx
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { FC, memo, useCallback } from "react"
|
||||||
|
import get from "lodash/get"
|
||||||
|
import styles from "./styles.module.scss"
|
||||||
|
import { useUserHosting } from "../hosting.utils"
|
||||||
|
import useAction from "../../../utils/useAction"
|
||||||
|
import { displayModal, MODAL_IDS } from "../../../store/ui"
|
||||||
|
|
||||||
|
const Hosting: FC = (): JSX.Element | null => {
|
||||||
|
const [userWishes] = useUserHosting()
|
||||||
|
const needsHosting = get(userWishes, "needsHosting", false)
|
||||||
|
const canHostCount = get(userWishes, "canHostCount", 0)
|
||||||
|
const distanceToFestival = get(userWishes, "distanceToFestival", 0)
|
||||||
|
const comment = get(userWishes, "hostingComment", "")
|
||||||
|
const execDisplayModal = useAction(displayModal)
|
||||||
|
const onEdit = useCallback(() => execDisplayModal(MODAL_IDS.HOSTING), [execDisplayModal])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.hosting}>
|
||||||
|
<div className={styles.title}>Mon hébergement</div>
|
||||||
|
{!needsHosting && (
|
||||||
|
<div className={styles.hostingLabel}>
|
||||||
|
Je n'ai pas besoin d'un hébergement proche du festival
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{needsHosting && (
|
||||||
|
<div className={styles.hostingLabel}>
|
||||||
|
J'ai <b>besoin</b> d'un hébergement proche du festival
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{canHostCount === 0 && distanceToFestival === 0 && (
|
||||||
|
<div className={styles.hostingLabel}>
|
||||||
|
Je ne peux héberger personnes de manière utile.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{canHostCount > 0 && (
|
||||||
|
<div className={styles.hostingLabel}>
|
||||||
|
Je peux héberger <b>{canHostCount} personnes</b> !
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{distanceToFestival > 0 && (
|
||||||
|
<div className={styles.hostingLabel}>
|
||||||
|
Je suis à <b>{distanceToFestival} minutes</b> du festival
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{comment && (
|
||||||
|
<div className={styles.commentLine}>
|
||||||
|
<span className={styles.commentLineTitle}>Mon commentaire :</span>
|
||||||
|
<span className={styles.commentLineText}>{comment}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className={styles.editButton}>
|
||||||
|
<button type="button" onClick={onEdit}>
|
||||||
|
Modifier
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(Hosting)
|
53
src/components/VolunteerBoard/Hosting/styles.module.scss
Executable file
53
src/components/VolunteerBoard/Hosting/styles.module.scss
Executable file
@ -0,0 +1,53 @@
|
|||||||
|
@import "../../../theme/variables";
|
||||||
|
@import "../../../theme/mixins";
|
||||||
|
|
||||||
|
.title {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hostingLabel {
|
||||||
|
margin-right: 5px;
|
||||||
|
font-style: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hosting {
|
||||||
|
@include inner-content-wrapper();
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
padding-right: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hostingLabel,
|
||||||
|
.commentLine {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
span {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.lineEmpty {
|
||||||
|
color: $color-red;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commentLineTitle {
|
||||||
|
padding-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commentLineText {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editButton {
|
||||||
|
@include vertical-center();
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
right: 20px;
|
||||||
|
|
||||||
|
button {
|
||||||
|
color: $color-green;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
133
src/components/VolunteerBoard/HostingForm/HostingForm.tsx
Normal file
133
src/components/VolunteerBoard/HostingForm/HostingForm.tsx
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
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 { useUserHosting } 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 [needsHosting, setNeedsHosting] = useState(false)
|
||||||
|
const canHostCountRef = useRef<HTMLInputElement | null>(null)
|
||||||
|
const distanceRef = useRef<HTMLInputElement | null>(null)
|
||||||
|
const commentRef = useRef<HTMLTextAreaElement | null>(null)
|
||||||
|
const [userWishes, saveWishes] = useUserHosting()
|
||||||
|
|
||||||
|
const onNeedsHostingChange = (e: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
setNeedsHosting(e.target.checked)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!userWishes) return
|
||||||
|
setNeedsHosting(get(userWishes, "needsHosting", false))
|
||||||
|
set(canHostCountRef, "current.value", `${get(userWishes, "canHostCount", 0)}`)
|
||||||
|
set(distanceRef, "current.value", `${get(userWishes, "distanceToFestival", 0)}`)
|
||||||
|
set(commentRef, "current.value", get(userWishes, "hostingComment", ""))
|
||||||
|
}, [commentRef, userWishes])
|
||||||
|
|
||||||
|
const onChoiceSubmit = useCallback(() => {
|
||||||
|
const canHostCount = +get(canHostCountRef, "current.value", "0")
|
||||||
|
const distanceToFestival = +get(distanceRef, "current.value", "0")
|
||||||
|
const hostingComment = get(commentRef, "current.value", "")
|
||||||
|
saveWishes(needsHosting, canHostCount, distanceToFestival, hostingComment)
|
||||||
|
if (afterSubmit) afterSubmit()
|
||||||
|
}, [needsHosting, commentRef, saveWishes, afterSubmit])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className={styles.title}>Mes jours de présence</div>
|
||||||
|
<div className={classnames(styles.inputWrapper, styles.noBottomMargin)}>
|
||||||
|
<div className={styles.leftCol}>
|
||||||
|
<div className={styles.needsHostingTitle}>
|
||||||
|
Cela t'arrangerait-il d'avoir un hébergement proche du festival ?
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.rightCol}>
|
||||||
|
<label className={styles.needsHostingLabel}>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
value="oui"
|
||||||
|
name="needsHosting"
|
||||||
|
onChange={onNeedsHostingChange}
|
||||||
|
checked={needsHosting}
|
||||||
|
/>{" "}
|
||||||
|
Oui
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{needsHosting && (
|
||||||
|
<div className={classnames(styles.inputWrapper, styles.noBottomMargin)}>
|
||||||
|
<div>
|
||||||
|
Il nous serait utile de savoir à quelle temps de transport tu te trouves
|
||||||
|
pour privilégier les bénévoles qui viennent de province à ceux qui viennent
|
||||||
|
de l'autre bout de Paris.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className={styles.inputWrapper}>
|
||||||
|
<div className={styles.leftCol}>
|
||||||
|
<div className={styles.canHostCountTitle}>
|
||||||
|
Combien de bénévoles peux-tu héberger confortablement ?
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.rightCol}>
|
||||||
|
<input className={styles.canHostCountLabel} type="text" ref={canHostCountRef} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.inputWrapper}>
|
||||||
|
<div className={styles.leftCol}>
|
||||||
|
<div className={styles.distanceToFestivalTitle}>
|
||||||
|
À combien de minutes de transport es-tu du festival ? (En voiture si tu es
|
||||||
|
en voiture, à vélo si tu as des vélos, sinon en transport en commun.)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.rightCol}>
|
||||||
|
<input
|
||||||
|
className={styles.distanceToFestivalLabel}
|
||||||
|
type="text"
|
||||||
|
ref={distanceRef}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.hostingCommentWrapper}>
|
||||||
|
<label htmlFor="hosting-comment">Un commentaire, une précision ?</label>
|
||||||
|
<textarea id="hosting-comment" ref={commentRef} />
|
||||||
|
</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">
|
||||||
|
{children}
|
||||||
|
</IgnoreButton>{" "}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
HostingForm.defaultProps = {
|
||||||
|
children: undefined,
|
||||||
|
afterSubmit: undefined,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(HostingForm)
|
||||||
|
|
||||||
|
// Fetch server-side data here
|
||||||
|
export const fetchFor = [fetchVolunteerHostingSetIfNeed]
|
@ -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 HostingForm from "./HostingForm"
|
||||||
|
|
||||||
|
const HostingFormModal: FC = (): JSX.Element => {
|
||||||
|
const execHideModal = useAction(hideModal)
|
||||||
|
const afterFormSubmit = useCallback(() => execHideModal(), [execHideModal])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal modalId={MODAL_IDS.HOSTING}>
|
||||||
|
<HostingForm afterSubmit={afterFormSubmit} />
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(HostingFormModal)
|
126
src/components/VolunteerBoard/HostingForm/styles.module.scss
Executable file
126
src/components/VolunteerBoard/HostingForm/styles.module.scss
Executable file
@ -0,0 +1,126 @@
|
|||||||
|
@import "../../../theme/variables";
|
||||||
|
@import "../../../theme/mixins";
|
||||||
|
|
||||||
|
.title {
|
||||||
|
padding: 15px 0;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputWrapper {
|
||||||
|
margin: 25px 0;
|
||||||
|
|
||||||
|
@include desktop {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.noBottomMargin {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.leftCol {
|
||||||
|
flex: 0 0 320px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rightCol {
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.needsHostingTitle {
|
||||||
|
display: inline-block;
|
||||||
|
width: 320px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.needsHostingLabel {
|
||||||
|
text-align: left;
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hostingTitle {
|
||||||
|
display: inline-block;
|
||||||
|
width: 320px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hostingList {
|
||||||
|
@include clear-ul-style;
|
||||||
|
|
||||||
|
display: inline-block;
|
||||||
|
width: 204px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hostingItem {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hostingButton {
|
||||||
|
margin: 0;
|
||||||
|
padding: 7px 2px 6px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
width: 90px;
|
||||||
|
text-align: center;
|
||||||
|
color: $color-grey-dark;
|
||||||
|
background-color: $color-grey-light;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: $color-yellow;
|
||||||
|
background-color: $color-black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hostingCommentWrapper {
|
||||||
|
margin: 6px 0 14px;
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
padding: 6px 0 2px 4px;
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
height: 50px;
|
||||||
|
padding: 5px;
|
||||||
|
border: 1px solid $color-grey-light;
|
||||||
|
background-color: $color-grey-lighter;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttonWrapper {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
31
src/components/VolunteerBoard/hosting.utils.ts
Normal file
31
src/components/VolunteerBoard/hosting.utils.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { shallowEqual, useSelector } from "react-redux"
|
||||||
|
import { useCallback } from "react"
|
||||||
|
import { selectUserJwtToken } from "../../store/auth"
|
||||||
|
import { AppState } from "../../store"
|
||||||
|
import { fetchVolunteerHostingSet } from "../../store/volunteerHostingSet"
|
||||||
|
import useAction from "../../utils/useAction"
|
||||||
|
|
||||||
|
export const useUserHosting = (): [any, any] => {
|
||||||
|
const save = useAction(fetchVolunteerHostingSet)
|
||||||
|
const jwtToken = useSelector(selectUserJwtToken)
|
||||||
|
const userWishes = useSelector(
|
||||||
|
(state: AppState) => state.volunteerHostingSet?.entity,
|
||||||
|
shallowEqual
|
||||||
|
)
|
||||||
|
|
||||||
|
const saveWishes = useCallback(
|
||||||
|
(needsHosting, canHostCount, distanceToFestival, hostingComment) => {
|
||||||
|
if (!userWishes) return
|
||||||
|
save(jwtToken, 0, {
|
||||||
|
id: userWishes.id,
|
||||||
|
needsHosting,
|
||||||
|
canHostCount,
|
||||||
|
distanceToFestival,
|
||||||
|
hostingComment,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
[userWishes, save, jwtToken]
|
||||||
|
)
|
||||||
|
|
||||||
|
return [userWishes, saveWishes]
|
||||||
|
}
|
@ -11,6 +11,7 @@ import {
|
|||||||
VolunteerTeamWishes,
|
VolunteerTeamWishes,
|
||||||
translationVolunteer,
|
translationVolunteer,
|
||||||
VolunteerDayWishes,
|
VolunteerDayWishes,
|
||||||
|
VolunteerHosting,
|
||||||
VolunteerParticipationDetails,
|
VolunteerParticipationDetails,
|
||||||
VolunteerTeamAssign,
|
VolunteerTeamAssign,
|
||||||
VolunteerKnowledge,
|
VolunteerKnowledge,
|
||||||
@ -324,6 +325,43 @@ export const volunteerDayWishesSet = expressAccessor.set(async (list, body, id)
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const volunteerHostingSet = expressAccessor.set(async (list, body, id) => {
|
||||||
|
const requestedId = +body[0] || id
|
||||||
|
if (requestedId !== id && requestedId !== 0) {
|
||||||
|
throw Error(`On ne peut acceder qu'à ses propres infos d'hébergement`)
|
||||||
|
}
|
||||||
|
const wishes = body[1] as VolunteerHosting
|
||||||
|
const volunteer: Volunteer | undefined = list.find((v) => v.id === requestedId)
|
||||||
|
if (!volunteer) {
|
||||||
|
throw Error(`Il n'y a aucun bénévole avec cet identifiant ${requestedId}`)
|
||||||
|
}
|
||||||
|
const newVolunteer = cloneDeep(volunteer)
|
||||||
|
|
||||||
|
if (wishes.needsHosting !== undefined) {
|
||||||
|
newVolunteer.needsHosting = wishes.needsHosting
|
||||||
|
}
|
||||||
|
if (wishes.canHostCount !== undefined) {
|
||||||
|
newVolunteer.canHostCount = wishes.canHostCount
|
||||||
|
}
|
||||||
|
if (wishes.distanceToFestival !== undefined) {
|
||||||
|
newVolunteer.distanceToFestival = wishes.distanceToFestival
|
||||||
|
}
|
||||||
|
if (wishes.hostingComment !== undefined) {
|
||||||
|
newVolunteer.hostingComment = wishes.hostingComment
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
toDatabase: newVolunteer,
|
||||||
|
toCaller: {
|
||||||
|
id: newVolunteer.id,
|
||||||
|
needsHosting: newVolunteer.needsHosting,
|
||||||
|
canHostCount: newVolunteer.canHostCount,
|
||||||
|
distanceToFestival: newVolunteer.distanceToFestival,
|
||||||
|
hostingComment: newVolunteer.hostingComment,
|
||||||
|
} as VolunteerHosting,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
export const volunteerParticipationDetailsSet = expressAccessor.set(async (list, body, id) => {
|
export const volunteerParticipationDetailsSet = expressAccessor.set(async (list, body, id) => {
|
||||||
const requestedId = +body[0] || id
|
const requestedId = +body[0] || id
|
||||||
if (requestedId !== id && requestedId !== 0) {
|
if (requestedId !== id && requestedId !== 0) {
|
||||||
|
@ -26,6 +26,7 @@ import {
|
|||||||
volunteerAsksSet,
|
volunteerAsksSet,
|
||||||
volunteerDayWishesSet,
|
volunteerDayWishesSet,
|
||||||
volunteerForgot,
|
volunteerForgot,
|
||||||
|
volunteerHostingSet,
|
||||||
volunteerDiscordId,
|
volunteerDiscordId,
|
||||||
volunteerLogin,
|
volunteerLogin,
|
||||||
volunteerPartialAdd,
|
volunteerPartialAdd,
|
||||||
@ -111,6 +112,7 @@ app.post(
|
|||||||
volunteerParticipationDetailsSet
|
volunteerParticipationDetailsSet
|
||||||
)
|
)
|
||||||
app.post("/VolunteerDayWishesSet", secure as RequestHandler, volunteerDayWishesSet)
|
app.post("/VolunteerDayWishesSet", secure as RequestHandler, volunteerDayWishesSet)
|
||||||
|
app.post("/VolunteerHostingSet", secure as RequestHandler, volunteerHostingSet)
|
||||||
app.post("/VolunteerTeamWishesSet", secure as RequestHandler, volunteerTeamWishesSet)
|
app.post("/VolunteerTeamWishesSet", secure as RequestHandler, volunteerTeamWishesSet)
|
||||||
app.post("/VolunteerTeamAssignSet", secure as RequestHandler, volunteerTeamAssignSet)
|
app.post("/VolunteerTeamAssignSet", secure as RequestHandler, volunteerTeamAssignSet)
|
||||||
|
|
||||||
|
@ -59,6 +59,14 @@ export class Volunteer implements VolunteerPartial {
|
|||||||
bof: number[] = []
|
bof: number[] = []
|
||||||
|
|
||||||
niet: number[] = []
|
niet: number[] = []
|
||||||
|
|
||||||
|
needsHosting = false
|
||||||
|
|
||||||
|
canHostCount = 0
|
||||||
|
|
||||||
|
distanceToFestival = 0
|
||||||
|
|
||||||
|
hostingComment = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
export const translationVolunteer: { [k in keyof Volunteer]: string } = {
|
export const translationVolunteer: { [k in keyof Volunteer]: string } = {
|
||||||
@ -92,6 +100,10 @@ export const translationVolunteer: { [k in keyof Volunteer]: string } = {
|
|||||||
ok: "OK",
|
ok: "OK",
|
||||||
bof: "Bof",
|
bof: "Bof",
|
||||||
niet: "Niet",
|
niet: "Niet",
|
||||||
|
needsHosting: "besoinHébergement",
|
||||||
|
canHostCount: "nombreHébergés",
|
||||||
|
distanceToFestival: "distanceAuFestival",
|
||||||
|
hostingComment: "commentaireHébergement",
|
||||||
}
|
}
|
||||||
|
|
||||||
export class VolunteerPartial {
|
export class VolunteerPartial {
|
||||||
@ -137,6 +149,10 @@ export const volunteerExample: Volunteer = {
|
|||||||
ok: [5, 7, 24, 26, 31, 38, 50, 52, 54, 58],
|
ok: [5, 7, 24, 26, 31, 38, 50, 52, 54, 58],
|
||||||
bof: [9, 12, 16, 27, 34, 35, 36],
|
bof: [9, 12, 16, 27, 34, 35, 36],
|
||||||
niet: [13, 18, 19, 23, 47, 53, 59, 67],
|
niet: [13, 18, 19, 23, 47, 53, 59, 67],
|
||||||
|
needsHosting: false,
|
||||||
|
canHostCount: 0,
|
||||||
|
distanceToFestival: 0,
|
||||||
|
hostingComment: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const emailRegexp =
|
export const emailRegexp =
|
||||||
@ -181,6 +197,14 @@ export interface VolunteerDayWishes {
|
|||||||
dayWishesComment: Volunteer["dayWishesComment"]
|
dayWishesComment: Volunteer["dayWishesComment"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface VolunteerHosting {
|
||||||
|
id: Volunteer["id"]
|
||||||
|
needsHosting: Volunteer["needsHosting"]
|
||||||
|
canHostCount: Volunteer["canHostCount"]
|
||||||
|
distanceToFestival: Volunteer["distanceToFestival"]
|
||||||
|
hostingComment: Volunteer["hostingComment"]
|
||||||
|
}
|
||||||
|
|
||||||
export interface VolunteerParticipationDetails {
|
export interface VolunteerParticipationDetails {
|
||||||
id: Volunteer["id"]
|
id: Volunteer["id"]
|
||||||
tshirtSize: Volunteer["tshirtSize"]
|
tshirtSize: Volunteer["tshirtSize"]
|
||||||
|
@ -3,6 +3,7 @@ import {
|
|||||||
elementName,
|
elementName,
|
||||||
Volunteer,
|
Volunteer,
|
||||||
VolunteerDayWishes,
|
VolunteerDayWishes,
|
||||||
|
VolunteerHosting,
|
||||||
VolunteerAsks,
|
VolunteerAsks,
|
||||||
VolunteerParticipationDetails,
|
VolunteerParticipationDetails,
|
||||||
VolunteerTeamWishes,
|
VolunteerTeamWishes,
|
||||||
@ -38,6 +39,9 @@ export const volunteerTeamWishesSet =
|
|||||||
export const volunteerDayWishesSet =
|
export const volunteerDayWishesSet =
|
||||||
serviceAccessors.securedCustomPost<[number, Partial<VolunteerDayWishes>]>("DayWishesSet")
|
serviceAccessors.securedCustomPost<[number, Partial<VolunteerDayWishes>]>("DayWishesSet")
|
||||||
|
|
||||||
|
export const volunteerHostingSet =
|
||||||
|
serviceAccessors.securedCustomPost<[number, Partial<VolunteerHosting>]>("HostingSet")
|
||||||
|
|
||||||
export const volunteerParticipationDetailsSet =
|
export const volunteerParticipationDetailsSet =
|
||||||
serviceAccessors.securedCustomPost<[number, Partial<VolunteerParticipationDetails>]>(
|
serviceAccessors.securedCustomPost<[number, Partial<VolunteerParticipationDetails>]>(
|
||||||
"ParticipationDetailsSet"
|
"ParticipationDetailsSet"
|
||||||
|
@ -16,6 +16,7 @@ import volunteerAsksSet from "./volunteerAsksSet"
|
|||||||
import volunteerDayWishesSet from "./volunteerDayWishesSet"
|
import volunteerDayWishesSet from "./volunteerDayWishesSet"
|
||||||
import volunteerDiscordId from "./volunteerDiscordId"
|
import volunteerDiscordId from "./volunteerDiscordId"
|
||||||
import volunteerForgot from "./volunteerForgot"
|
import volunteerForgot from "./volunteerForgot"
|
||||||
|
import volunteerHostingSet from "./volunteerHostingSet"
|
||||||
import volunteerList from "./volunteerList"
|
import volunteerList from "./volunteerList"
|
||||||
import volunteerLogin from "./volunteerLogin"
|
import volunteerLogin from "./volunteerLogin"
|
||||||
import volunteerKnowledgeSet from "./volunteerKnowledgeSet"
|
import volunteerKnowledgeSet from "./volunteerKnowledgeSet"
|
||||||
@ -44,6 +45,7 @@ export default (history: History) => ({
|
|||||||
volunteerDayWishesSet,
|
volunteerDayWishesSet,
|
||||||
volunteerDiscordId,
|
volunteerDiscordId,
|
||||||
volunteerForgot,
|
volunteerForgot,
|
||||||
|
volunteerHostingSet,
|
||||||
volunteerList,
|
volunteerList,
|
||||||
volunteerLogin,
|
volunteerLogin,
|
||||||
volunteerKnowledgeSet,
|
volunteerKnowledgeSet,
|
||||||
|
@ -28,6 +28,7 @@ export const selectActiveModalId = createSelector(selectUiData, (ui) => ui.modal
|
|||||||
|
|
||||||
export const MODAL_IDS = {
|
export const MODAL_IDS = {
|
||||||
DAYWISHES: "DAYWISHES",
|
DAYWISHES: "DAYWISHES",
|
||||||
|
HOSTING: "HOSTING",
|
||||||
PARTICIPATIONDETAILS: "PARTICIPATIONDETAILS",
|
PARTICIPATIONDETAILS: "PARTICIPATIONDETAILS",
|
||||||
TEAMWISHES: "TEAMWISHES",
|
TEAMWISHES: "TEAMWISHES",
|
||||||
}
|
}
|
||||||
|
60
src/store/volunteerHostingSet.ts
Normal file
60
src/store/volunteerHostingSet.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { PayloadAction, createSlice } from "@reduxjs/toolkit"
|
||||||
|
|
||||||
|
import { StateRequest, toastError, elementFetch } from "./utils"
|
||||||
|
import { VolunteerHosting } from "../services/volunteers"
|
||||||
|
import { AppThunk, AppState } from "."
|
||||||
|
import { volunteerHostingSet } from "../services/volunteersAccessors"
|
||||||
|
|
||||||
|
type StateVolunteerHostingSet = { entity?: VolunteerHosting } & StateRequest
|
||||||
|
|
||||||
|
export const initialState: StateVolunteerHostingSet = {
|
||||||
|
readyStatus: "idle",
|
||||||
|
}
|
||||||
|
|
||||||
|
const volunteerHostingSetSlice = createSlice({
|
||||||
|
name: "volunteerHostingSet",
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
getRequesting: (_) => ({
|
||||||
|
readyStatus: "request",
|
||||||
|
}),
|
||||||
|
getSuccess: (_, { payload }: PayloadAction<VolunteerHosting>) => ({
|
||||||
|
readyStatus: "success",
|
||||||
|
entity: payload,
|
||||||
|
}),
|
||||||
|
getFailure: (_, { payload }: PayloadAction<string>) => ({
|
||||||
|
readyStatus: "failure",
|
||||||
|
error: payload,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default volunteerHostingSetSlice.reducer
|
||||||
|
export const { getRequesting, getSuccess, getFailure } = volunteerHostingSetSlice.actions
|
||||||
|
|
||||||
|
export const fetchVolunteerHostingSet = elementFetch(
|
||||||
|
volunteerHostingSet,
|
||||||
|
getRequesting,
|
||||||
|
getSuccess,
|
||||||
|
getFailure,
|
||||||
|
(error: Error) =>
|
||||||
|
toastError(`Erreur lors du chargement des choix de jours de présence: ${error.message}`)
|
||||||
|
)
|
||||||
|
|
||||||
|
const shouldFetchVolunteerHostingSet = (state: AppState, id: number) =>
|
||||||
|
state.volunteerHostingSet?.readyStatus !== "success" ||
|
||||||
|
(state.volunteerHostingSet?.entity && state.volunteerHostingSet?.entity?.id !== id)
|
||||||
|
|
||||||
|
export const fetchVolunteerHostingSetIfNeed =
|
||||||
|
(id = 0, wishes: Partial<VolunteerHosting> = {}): AppThunk =>
|
||||||
|
(dispatch, getState) => {
|
||||||
|
let jwt = ""
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
;({ jwt, id } = getState().auth)
|
||||||
|
}
|
||||||
|
if (shouldFetchVolunteerHostingSet(getState(), id))
|
||||||
|
return dispatch(fetchVolunteerHostingSet(jwt, id, wishes))
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user