mirror of
https://github.com/Paris-est-Ludique/intranet.git
synced 2025-09-11 05:46:28 +02:00
Add PersonalInfo form and ask; Fix Meals
This commit is contained in:
36
src/components/Asks/AskPersonalInfo.tsx
Normal file
36
src/components/Asks/AskPersonalInfo.tsx
Normal file
@@ -0,0 +1,36 @@
|
||||
import { get } from "lodash"
|
||||
import { useCallback } from "react"
|
||||
import { fetchVolunteerAsksSet } from "../../store/volunteerAsksSet"
|
||||
import { useAskTools, addAsk, answerLaterOnProfile } from "./utils"
|
||||
import PersonalInfoForm, {
|
||||
fetchFor as fetchForPersonalInfoForm,
|
||||
} from "../VolunteerBoard/PersonalInfoForm/PersonalInfoForm"
|
||||
import { useUserPersonalInfo } from "../VolunteerBoard/personalInfo.utils"
|
||||
|
||||
export function AskPersonalInfo(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 [personalInfo] = useUserPersonalInfo()
|
||||
const photo = get(personalInfo, "photo", false)
|
||||
const needToShow = !/^[0-9]/.test(photo || "")
|
||||
|
||||
addAsk(
|
||||
asks,
|
||||
id,
|
||||
volunteerAsks,
|
||||
false,
|
||||
needToShow,
|
||||
<PersonalInfoForm afterSubmit={onSubmit}>{answerLaterOnProfile}</PersonalInfoForm>
|
||||
)
|
||||
}
|
||||
|
||||
// Fetch server-side data here
|
||||
export const fetchFor = [...fetchForPersonalInfoForm]
|
@@ -6,7 +6,8 @@ import { AskWelcome } from "./AskWelcome"
|
||||
import { AskDiscord, fetchFor as fetchForDiscord } from "./AskDiscord"
|
||||
import { AskDayWishes, fetchFor as fetchForDayWishes } from "./AskDayWishes"
|
||||
import { AskHosting, fetchFor as fetchForHosting } from "./AskHosting"
|
||||
import { AskMeals, fetchFor as fetchForMeals } from "./AskMeals"
|
||||
// import { AskMeals, fetchFor as fetchForMeals } from "./AskMeals"
|
||||
import { AskPersonalInfo, fetchFor as fetchForPersonalInfo } from "./AskPersonalInfo"
|
||||
import { AskTeamWishes, fetchFor as fetchForTeamWishes } from "./AskTeamWishes"
|
||||
import {
|
||||
AskParticipationDetails,
|
||||
@@ -24,8 +25,9 @@ const Asks = (): JSX.Element | null => {
|
||||
AskDayWishes(asks, 10)
|
||||
AskTeamWishes(asks, 11)
|
||||
AskParticipationDetails(asks, 12)
|
||||
AskPersonalInfo(asks, 15)
|
||||
AskHosting(asks, 20)
|
||||
AskMeals(asks, 22)
|
||||
// AskMeals(asks, 22)
|
||||
|
||||
AskPushNotif(asks, 99)
|
||||
|
||||
@@ -63,7 +65,8 @@ export const fetchFor = [
|
||||
...fetchForDiscord,
|
||||
...fetchForDayWishes,
|
||||
...fetchForHosting,
|
||||
...fetchForMeals,
|
||||
// ...fetchForMeals,
|
||||
...fetchForTeamWishes,
|
||||
...fetchForParticipationDetails,
|
||||
...fetchForPersonalInfo,
|
||||
]
|
||||
|
@@ -30,6 +30,7 @@
|
||||
max-height: 80vh;
|
||||
border-radius: 15px;
|
||||
border: $border-large;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -48,7 +48,6 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
|
||||
const [firstMeeting, setFirstMeeting] = useState("")
|
||||
const [commentFirstMeeting, setCommentFirstMeeting] = useState("")
|
||||
const [canHelpBefore, setCanHelpBefore] = useState("")
|
||||
const [pelMember, setPelMember] = useState(false)
|
||||
const [howToContact, setHowToContact] = useState("Email")
|
||||
const [sending, setSending] = useState(false)
|
||||
const [changingBackground, setChangingBackground] = useState(0)
|
||||
@@ -107,7 +106,6 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
|
||||
mobile,
|
||||
howToContact,
|
||||
canHelpBefore,
|
||||
pelMember,
|
||||
})
|
||||
)
|
||||
dispatch(
|
||||
@@ -623,54 +621,20 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
|
||||
</>
|
||||
)
|
||||
|
||||
const pelMemberQuestion = enableRegistering && !potentialVolunteer && (
|
||||
const pelMember = enableRegistering && (
|
||||
<>
|
||||
<dl className={styles.registerIntro}>
|
||||
<dt>Association Paris est Ludique</dt>
|
||||
<dd>
|
||||
<p>
|
||||
Légalement il faut que le festival soit organisé par une structure, et c'est
|
||||
l'association <i>Paris est Ludique !</i> qui s'en charge. Pour aider à
|
||||
organiser bénévolement le festival il faut donc en faire partie. Ça n'engage
|
||||
à rien et c'est gratuit, mais absolument nécessaire.
|
||||
l'association <i>Paris est Ludique !</i> qui s'en charge. Pour avoir un
|
||||
droit de regard dessus, devenir bénévole à cette édition implique
|
||||
automatiquement d'en devenir membre jusqu'à septembre prochain. Ça n'engage
|
||||
à rien et c'est gratuit !
|
||||
</p>
|
||||
</dd>
|
||||
</dl>
|
||||
<div className={styles.inputWrapper}>
|
||||
<div className={styles.leftCol}>
|
||||
<div className={styles.multipleChoiceTitle}>
|
||||
Acceptes-tu de devenir membre de l'association <i>Paris est Ludique !</i> ?
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.rightCol}>
|
||||
<div className={styles.rightColContainer}>
|
||||
{["Oui", "Non"].map((option) => (
|
||||
<label className={styles.shortAnswerLabel} key={option}>
|
||||
<input
|
||||
type="radio"
|
||||
name="pelMember"
|
||||
onChange={sendBooleanRadioboxDispatch(
|
||||
setPelMember,
|
||||
option === "Oui"
|
||||
)}
|
||||
checked={pelMember === (option === "Oui")}
|
||||
/>{" "}
|
||||
{option}
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{!pelMember && (
|
||||
<dl className={styles.registerIntro}>
|
||||
<dd>
|
||||
<p>
|
||||
Tant que tu n'as pas accepté cette condition je suis désolé on ne peut
|
||||
pas continuer.
|
||||
</p>
|
||||
</dd>
|
||||
</dl>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -803,14 +767,10 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
|
||||
{cameAsVisitor}
|
||||
{meeting}
|
||||
{helpBefore}
|
||||
{pelMemberQuestion}
|
||||
{(potentialVolunteer || pelMember) && (
|
||||
<>
|
||||
{nameMobileEmail}
|
||||
{!enableRegistering && commentQuestion}
|
||||
{howToContact !== "Aucun" && submitButton}
|
||||
</>
|
||||
)}
|
||||
{pelMember}
|
||||
{nameMobileEmail}
|
||||
{!enableRegistering && commentQuestion}
|
||||
{howToContact !== "Aucun" && submitButton}
|
||||
</>
|
||||
)}
|
||||
</form>
|
||||
|
@@ -34,7 +34,7 @@ type VolunteerEmailProps = {
|
||||
|
||||
const VolunteerEmail: FC<VolunteerEmailProps> = withUserRole(ROLES.TEAMLEAD, ({ email }) => (
|
||||
<td> {email}</td>
|
||||
))
|
||||
), null)
|
||||
|
||||
type DaysAvailabilityProps = {
|
||||
volunteer: Volunteer
|
||||
|
@@ -15,12 +15,17 @@ 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 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"
|
||||
|
||||
const Board: FC = (): JSX.Element => (
|
||||
<>
|
||||
<ContentTitle title="Profil spécifique au festival" />
|
||||
<PersonalInfo />
|
||||
<PersonalInfoFormModal />
|
||||
<DayWishes />
|
||||
<DayWishesFormModal />
|
||||
<ParticipationDetails />
|
||||
@@ -42,5 +47,6 @@ export const fetchFor = [
|
||||
...fetchForHostingForm,
|
||||
...fetchForMealsForm,
|
||||
...fetchForParticipationDetailsForm,
|
||||
...fetchForPersonalInfoForm,
|
||||
...fetchForTeamWishesForm,
|
||||
]
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import { FC, memo, useCallback } from "react"
|
||||
import { FC, memo } from "react"
|
||||
import { find, get } from "lodash"
|
||||
import styles from "./styles.module.scss"
|
||||
import { useUserMeals, mealDays, MealOption } from "../meals.utils"
|
||||
import useAction from "../../../utils/useAction"
|
||||
import { displayModal, MODAL_IDS } from "../../../store/ui"
|
||||
// import useAction from "../../../utils/useAction"
|
||||
// import { displayModal, MODAL_IDS } from "../../../store/ui"
|
||||
import { useUserDayWishes } from "../daysWishes.utils"
|
||||
|
||||
const Meals: FC = (): JSX.Element | null => {
|
||||
@@ -11,8 +11,8 @@ const Meals: FC = (): JSX.Element | null => {
|
||||
const [userWishes] = useUserDayWishes()
|
||||
const meals = get(userMeals, "meals", [])
|
||||
const dayWishesString = get(userWishes, "dayWishes", [])
|
||||
const execDisplayModal = useAction(displayModal)
|
||||
const onEdit = useCallback(() => execDisplayModal(MODAL_IDS.MEALS), [execDisplayModal])
|
||||
// const execDisplayModal = useAction(displayModal)
|
||||
// const onEdit = useCallback(() => execDisplayModal(MODAL_IDS.MEALS), [execDisplayModal])
|
||||
const mealChoices = mealDays.map((meal, i: number) =>
|
||||
find(meal.options, { abbr: meals[i] || "" })
|
||||
) as MealOption[]
|
||||
@@ -53,10 +53,13 @@ const Meals: FC = (): JSX.Element | null => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className={styles.editButton} key="edit">
|
||||
{/* <div className={styles.editButton} key="edit">
|
||||
<button type="button" onClick={onEdit}>
|
||||
Modifier
|
||||
</button>
|
||||
</div> */}
|
||||
<div className={styles.editButton} key="edit">
|
||||
Plus modifiable
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
@@ -46,11 +46,8 @@ const MealsForm: FC<Props> = ({ children, afterSubmit }): JSX.Element => {
|
||||
<div className={styles.leftCol}>
|
||||
<div className={styles.needsMealsTitle}>
|
||||
<b>{mealDays[i].name}</b>
|
||||
{i === 0 && <>, accompagné d'un délicieux brownie tout chocolat</>}
|
||||
{i === 1 && (
|
||||
<>, accompagné du même brownie. Enfin, un autre que celui de la veille</>
|
||||
)}{" "}
|
||||
:
|
||||
{(i === 0 || i === 1) && <>, accompagné d'un délicieux brownie tout chocolat</>}
|
||||
{i === 2 && <>, accompagné d'une part de tarte indéterminée</>} :
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.rightCol}>
|
||||
@@ -89,6 +86,13 @@ const MealsForm: FC<Props> = ({ children, afterSubmit }): JSX.Element => {
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.title}>Mes repas</div>
|
||||
<div className={classnames(styles.inputWrapper)}>
|
||||
<div>
|
||||
La composition exacte des repas ne sera pas connues avant le festival (risque de
|
||||
changement d'ingrédient au dernier moment). Elle sera disponible au moment de
|
||||
récupérer ton repas pour que tu puisses contrôler l'absence d'allergène.
|
||||
</div>
|
||||
</div>
|
||||
{dayWishesString.includes("S") ? (
|
||||
<>
|
||||
{getBreakfeastElement("Samedi")}
|
||||
|
40
src/components/VolunteerBoard/PersonalInfo/PersonalInfo.tsx
Normal file
40
src/components/VolunteerBoard/PersonalInfo/PersonalInfo.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import { FC, memo, useCallback } from "react"
|
||||
import get from "lodash/get"
|
||||
import styles from "./styles.module.scss"
|
||||
import { useUserPersonalInfo } from "../personalInfo.utils"
|
||||
import useAction from "../../../utils/useAction"
|
||||
import { displayModal, MODAL_IDS } from "../../../store/ui"
|
||||
|
||||
const PersonalInfo: FC = (): JSX.Element | null => {
|
||||
const [userWishes] = useUserPersonalInfo()
|
||||
const firstname = get(userWishes, "firstname", "")
|
||||
const lastname = get(userWishes, "lastname", "")
|
||||
const photo = get(userWishes, "photo", "")
|
||||
const execDisplayModal = useAction(displayModal)
|
||||
const onEdit = useCallback(() => execDisplayModal(MODAL_IDS.PERSONALINFO), [execDisplayModal])
|
||||
|
||||
return (
|
||||
<div className={styles.personalInfo}>
|
||||
<div className={styles.title}>Mes infos personnelles</div>
|
||||
<div className={styles.personalInfoLabel}>
|
||||
{firstname || "Aucun prénom"} {lastname || "Aucun nom de famille"}
|
||||
</div>
|
||||
|
||||
{/^[0-9]/.test(photo || "") && (
|
||||
<div className={styles.personalInfoLabel}>Ta photo est bien renseignée merci !</div>
|
||||
)}
|
||||
|
||||
{!/^[0-9]/.test(photo || "") && (
|
||||
<div className={styles.personalInfoLabel}>Il faudrait renseigner ta photo !</div>
|
||||
)}
|
||||
|
||||
<div className={styles.editButton}>
|
||||
<button type="button" onClick={onEdit}>
|
||||
Modifier
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(PersonalInfo)
|
53
src/components/VolunteerBoard/PersonalInfo/styles.module.scss
Executable file
53
src/components/VolunteerBoard/PersonalInfo/styles.module.scss
Executable file
@@ -0,0 +1,53 @@
|
||||
@import "../../../theme/variables";
|
||||
@import "../../../theme/mixins";
|
||||
|
||||
.title {
|
||||
padding-bottom: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.personalInfoLabel {
|
||||
margin-right: 5px;
|
||||
font-style: bold;
|
||||
}
|
||||
|
||||
.personalInfo {
|
||||
@include inner-content-wrapper();
|
||||
|
||||
position: relative;
|
||||
padding-right: 90px;
|
||||
}
|
||||
|
||||
.personalInfoLabel,
|
||||
.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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,155 @@
|
||||
import { FC, memo, ReactNode, useCallback, useEffect, useRef, useState } from "react"
|
||||
import classnames from "classnames"
|
||||
import { get, set } from "lodash"
|
||||
import styles from "./styles.module.scss"
|
||||
import { useUserPersonalInfo } from "../personalInfo.utils"
|
||||
import FormButton from "../../Form/FormButton/FormButton"
|
||||
import { fetchVolunteerPersonalInfoSetIfNeed } from "../../../store/volunteerPersonalInfoSet"
|
||||
import IgnoreButton from "../../Form/IgnoreButton/IgnoreButton"
|
||||
|
||||
type Props = {
|
||||
children?: ReactNode | undefined
|
||||
afterSubmit?: () => void | undefined
|
||||
}
|
||||
|
||||
const PersonalInfoForm: FC<Props> = ({ children, afterSubmit }): JSX.Element => {
|
||||
const firstnameRef = useRef<HTMLInputElement | null>(null)
|
||||
const lastnameRef = useRef<HTMLInputElement | null>(null)
|
||||
const [photo, setPhoto] = useState("")
|
||||
const [selectedImage, setSelectedImage] = useState<File | null>(null)
|
||||
const [userWishes, saveWishes] = useUserPersonalInfo()
|
||||
|
||||
useEffect(() => {
|
||||
if (!userWishes) return
|
||||
set(firstnameRef, "current.value", get(userWishes, "firstname", ""))
|
||||
set(lastnameRef, "current.value", get(userWishes, "lastname", ""))
|
||||
setPhoto(get(userWishes, "photo", ""))
|
||||
}, [userWishes])
|
||||
|
||||
const onChoiceSubmit = useCallback(() => {
|
||||
const firstname = get(firstnameRef, "current.value", "")
|
||||
const lastname = get(lastnameRef, "current.value", "")
|
||||
const reader = new FileReader()
|
||||
reader.onload = async (event) => {
|
||||
const photoData = event?.target?.result as string
|
||||
if (!photoData) {
|
||||
throw Error("Ce n'est pas une photo valide")
|
||||
}
|
||||
saveWishes(firstname, lastname, photoData)
|
||||
if (afterSubmit) afterSubmit()
|
||||
}
|
||||
if (selectedImage) {
|
||||
reader.readAsDataURL(selectedImage)
|
||||
} else {
|
||||
saveWishes(firstname, lastname, undefined)
|
||||
if (afterSubmit) afterSubmit()
|
||||
}
|
||||
}, [selectedImage, saveWishes, afterSubmit])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.title}>Mes infos personnelles</div>
|
||||
|
||||
<div className={styles.inputWrapper}>
|
||||
<div className={styles.leftCol}>
|
||||
<div className={styles.firstnameTitle}>Prénom :</div>
|
||||
</div>
|
||||
<div className={styles.rightCol}>
|
||||
<input className={styles.firstnameLabel} type="text" ref={firstnameRef} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.inputWrapper}>
|
||||
<div className={styles.leftCol}>
|
||||
<div className={styles.lastnameTitle}>Nom :</div>
|
||||
</div>
|
||||
<div className={styles.rightCol}>
|
||||
<input className={styles.lastnameLabel} type="text" ref={lastnameRef} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={classnames(styles.inputWrapper, styles.noBottomMargin)}>
|
||||
<div className={styles.leftCol}>
|
||||
<div className={styles.photoTitle}>
|
||||
Ta photo de profil pour le trombinoscope de l'association :
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.rightCol}>
|
||||
<label className={styles.photoLabel}>
|
||||
{/^[0-9]/.test(photo || "") && !selectedImage && (
|
||||
<div>
|
||||
<img alt="actuelle" width="100px" src={`/photos/${photo}`} />
|
||||
</div>
|
||||
)}
|
||||
{selectedImage && (
|
||||
<div>
|
||||
<img
|
||||
alt="remplacement"
|
||||
width="100px"
|
||||
src={URL.createObjectURL(selectedImage)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<br />
|
||||
|
||||
<input
|
||||
type="file"
|
||||
name="myImage"
|
||||
onChange={(event) => {
|
||||
console.log(event?.target?.files?.[0])
|
||||
setSelectedImage(event?.target?.files?.[0] || null)
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.inputWrapper}>
|
||||
<div>
|
||||
En tant que bénévole pour le festival, tu es automatiquement membre adhérent de
|
||||
l'association Paris est Ludique ! pour avoir droit de regard sur son
|
||||
fonctionnement. Aucune cotisation n'est demandée et aucun engagement autre
|
||||
qu'être bénévole pendant le festival n'est nécessaire. Les statuts sont{" "}
|
||||
<a
|
||||
href="https://drive.google.com/file/d/1KJIJxZmDdJ_PBKJSh_W3yrHLzH0-awsE/view?usp=sharing"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
accessibles ici
|
||||
</a>
|
||||
.
|
||||
</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">
|
||||
{children}
|
||||
</IgnoreButton>{" "}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
PersonalInfoForm.defaultProps = {
|
||||
children: undefined,
|
||||
afterSubmit: undefined,
|
||||
}
|
||||
|
||||
export default memo(PersonalInfoForm)
|
||||
|
||||
// Fetch server-side data here
|
||||
export const fetchFor = [fetchVolunteerPersonalInfoSetIfNeed]
|
@@ -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 PersonalInfoForm from "./PersonalInfoForm"
|
||||
|
||||
const PersonalInfoFormModal: FC = (): JSX.Element => {
|
||||
const execHideModal = useAction(hideModal)
|
||||
const afterFormSubmit = useCallback(() => execHideModal(), [execHideModal])
|
||||
|
||||
return (
|
||||
<Modal modalId={MODAL_IDS.PERSONALINFO}>
|
||||
<PersonalInfoForm afterSubmit={afterFormSubmit} />
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(PersonalInfoFormModal)
|
123
src/components/VolunteerBoard/PersonalInfoForm/styles.module.scss
Executable file
123
src/components/VolunteerBoard/PersonalInfoForm/styles.module.scss
Executable file
@@ -0,0 +1,123 @@
|
||||
@import "../../../theme/variables";
|
||||
@import "../../../theme/mixins";
|
||||
|
||||
.title {
|
||||
padding: 15px 0;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.inputWrapper {
|
||||
margin: 25px 0;
|
||||
|
||||
@include desktop {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.noTopMargin {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.noBottomMargin {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.leftCol {
|
||||
flex: 0 0 220px;
|
||||
}
|
||||
|
||||
.rightCol {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.firstnameTitle,
|
||||
.lastnameTitle {
|
||||
display: inline-block;
|
||||
width: 80px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.firstnameLabel,
|
||||
.lastnameLabel {
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.photoTitle {
|
||||
display: inline-block;
|
||||
width: 180px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.photoLabel {
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
margin-bottom: 10px;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.personalInfoTitle {
|
||||
display: inline-block;
|
||||
width: 320px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.personalInfoList {
|
||||
@include clear-ul-style;
|
||||
|
||||
display: inline-block;
|
||||
width: 204px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.personalInfoItem {
|
||||
display: inline-block;
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
.personalInfoButton {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
.personalInfoCommentWrapper {
|
||||
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;
|
||||
}
|
||||
|
||||
.yesMembership {
|
||||
color: $color-orange;
|
||||
}
|
37
src/components/VolunteerBoard/personalInfo.utils.ts
Normal file
37
src/components/VolunteerBoard/personalInfo.utils.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { shallowEqual, useSelector } from "react-redux"
|
||||
import { useCallback } from "react"
|
||||
import { selectUserJwtToken } from "../../store/auth"
|
||||
import { AppState } from "../../store"
|
||||
import { fetchVolunteerPersonalInfoSet } from "../../store/volunteerPersonalInfoSet"
|
||||
import useAction from "../../utils/useAction"
|
||||
import { VolunteerPersonalInfo } from "../../services/volunteers"
|
||||
|
||||
type SetFunction = (
|
||||
firstname: VolunteerPersonalInfo["firstname"],
|
||||
lastname: VolunteerPersonalInfo["lastname"],
|
||||
photo: VolunteerPersonalInfo["photo"] | undefined
|
||||
) => void
|
||||
|
||||
export const useUserPersonalInfo = (): [VolunteerPersonalInfo | undefined, SetFunction] => {
|
||||
const save = useAction(fetchVolunteerPersonalInfoSet)
|
||||
const jwtToken = useSelector(selectUserJwtToken)
|
||||
const userWishes = useSelector(
|
||||
(state: AppState) => state.volunteerPersonalInfoSet?.entity,
|
||||
shallowEqual
|
||||
)
|
||||
|
||||
const saveWishes = useCallback(
|
||||
(firstname, lastname, photo) => {
|
||||
if (!userWishes) return
|
||||
save(jwtToken, 0, {
|
||||
id: userWishes.id,
|
||||
firstname,
|
||||
lastname,
|
||||
photo,
|
||||
})
|
||||
},
|
||||
[userWishes, save, jwtToken]
|
||||
)
|
||||
|
||||
return [userWishes, saveWishes]
|
||||
}
|
Reference in New Issue
Block a user