mirror of
https://github.com/Paris-est-Ludique/intranet.git
synced 2025-06-09 09:04:20 +02:00
Fix tests & improve pre-register
This commit is contained in:
parent
fde48e6cbb
commit
d63f906206
@ -41,9 +41,8 @@
|
||||
"scripts": {
|
||||
"dev": "yarn dev:build && nodemon ./public/server",
|
||||
"dev:build": "cross-env NODE_ENV=development webpack --config ./webpack/server.config.ts",
|
||||
"local-start": "cross-env LOCAL=true node ./public/server",
|
||||
"local-start": "cross-env LOCAL=true yarn build && node ./public/server",
|
||||
"start": "node ./public/server",
|
||||
"local-build": "cross-env LOCAL=true run-s build:*",
|
||||
"build": "run-s build:*",
|
||||
"build:server": "cross-env NODE_ENV=production webpack --config ./webpack/server.config.ts",
|
||||
"build:client": "cross-env NODE_ENV=production webpack --config ./webpack/client.config.ts",
|
||||
|
@ -9,52 +9,52 @@ exports[`<AddEnvie /> renders 1`] = `
|
||||
</h2>
|
||||
<form>
|
||||
<label
|
||||
for="postDomaine"
|
||||
for="postDomain"
|
||||
>
|
||||
Domaine:
|
||||
<input
|
||||
id="postDomaine"
|
||||
name="postDomaine"
|
||||
id="postDomain"
|
||||
name="postDomain"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postEnvies"
|
||||
for="postWish"
|
||||
>
|
||||
Envies:
|
||||
<textarea
|
||||
id="postEnvies"
|
||||
name="postEnvies"
|
||||
id="postWish"
|
||||
name="postWish"
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postPrecisions"
|
||||
for="postDetails"
|
||||
>
|
||||
Precisions:
|
||||
<textarea
|
||||
id="postPrecisions"
|
||||
name="postPrecisions"
|
||||
id="postDetails"
|
||||
name="postDetails"
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postEquipes"
|
||||
for="postTeams"
|
||||
>
|
||||
Equipes:
|
||||
<input
|
||||
id="postEquipes"
|
||||
name="postEquipes"
|
||||
id="postTeams"
|
||||
name="postTeams"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postDateAjout"
|
||||
for="postAddedDate"
|
||||
>
|
||||
DateAjout:
|
||||
Date dajout:
|
||||
<input
|
||||
id="postDateAjout"
|
||||
name="postDateAjout"
|
||||
id="postAddedDate"
|
||||
name="postAddedDate"
|
||||
type="date"
|
||||
value=""
|
||||
/>
|
||||
|
@ -1,50 +1,49 @@
|
||||
import React, { useState, memo } from "react"
|
||||
import { toast } from "react-toastify"
|
||||
import styles from "./styles.module.scss"
|
||||
|
||||
import { AppDispatch } from "../../store"
|
||||
|
||||
import { fetchEnvieAdd } from "../../store/envieAdd"
|
||||
import styles from "./styles.module.scss"
|
||||
|
||||
interface Props {
|
||||
dispatch: AppDispatch
|
||||
}
|
||||
|
||||
const AddEnvie = ({ dispatch }: Props) => {
|
||||
const [domaine, setDomaine] = useState("")
|
||||
const [envies, setEnvies] = useState("")
|
||||
const [precisions, setPrecisions] = useState("")
|
||||
const [equipes, setEquipes] = useState([""])
|
||||
const [dateAjout, setDateAjout] = useState("")
|
||||
const [domain, setDomain] = useState("")
|
||||
const [wish, setWish] = useState("")
|
||||
const [details, setDetails] = useState("")
|
||||
const [teams, setTeams] = useState([""])
|
||||
const [addedDate, setAddedDate] = useState("")
|
||||
|
||||
const onDomaineChanged = (e: React.ChangeEvent<HTMLInputElement>) => setDomaine(e.target.value)
|
||||
const onEnviesChanged = (e: React.ChangeEvent<HTMLTextAreaElement>) => setEnvies(e.target.value)
|
||||
const onPrecisionsChanged = (e: React.ChangeEvent<HTMLTextAreaElement>) =>
|
||||
setPrecisions(e.target.value)
|
||||
const onEquipesChanged = (e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setEquipes(e.target.value.split(/, ?/))
|
||||
const onDateAjoutChanged = (e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setDateAjout(e.target.value)
|
||||
const onDomainChanged = (e: React.ChangeEvent<HTMLInputElement>) => setDomain(e.target.value)
|
||||
const onWishChanged = (e: React.ChangeEvent<HTMLTextAreaElement>) => setWish(e.target.value)
|
||||
const onDetailsChanged = (e: React.ChangeEvent<HTMLTextAreaElement>) =>
|
||||
setDetails(e.target.value)
|
||||
const onTeamsChanged = (e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setTeams(e.target.value.split(/, ?/))
|
||||
const onAddedDateChanged = (e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setAddedDate(e.target.value)
|
||||
|
||||
const onSavePostClicked = () => {
|
||||
if (domaine && envies) {
|
||||
if (domain && wish) {
|
||||
dispatch(
|
||||
fetchEnvieAdd({
|
||||
domaine,
|
||||
envies,
|
||||
precisions,
|
||||
equipes,
|
||||
dateAjout,
|
||||
domain,
|
||||
wish,
|
||||
details,
|
||||
teams,
|
||||
addedDate,
|
||||
})
|
||||
)
|
||||
|
||||
setDomaine("")
|
||||
setEnvies("")
|
||||
setPrecisions("")
|
||||
setEquipes([""])
|
||||
setDateAjout("")
|
||||
setDomain("")
|
||||
setWish("")
|
||||
setDetails("")
|
||||
setTeams([""])
|
||||
setAddedDate("")
|
||||
} else {
|
||||
toast.warning("Il faut au moins préciser un domaine et l'envie", {
|
||||
toast.warning("Il faut au moins préciser un domain et l'envie", {
|
||||
position: "top-center",
|
||||
autoClose: 6000,
|
||||
hideProgressBar: true,
|
||||
@ -59,52 +58,47 @@ const AddEnvie = ({ dispatch }: Props) => {
|
||||
<section className={styles.EnvieList}>
|
||||
<h2>Ajouter une nouvelle envie</h2>
|
||||
<form>
|
||||
<label htmlFor="postDomaine">
|
||||
<label htmlFor="postDomain">
|
||||
Domaine:
|
||||
<input
|
||||
type="text"
|
||||
id="postDomaine"
|
||||
name="postDomaine"
|
||||
value={domaine}
|
||||
onChange={onDomaineChanged}
|
||||
id="postDomain"
|
||||
name="postDomain"
|
||||
value={domain}
|
||||
onChange={onDomainChanged}
|
||||
/>
|
||||
</label>
|
||||
<label htmlFor="postEnvies">
|
||||
<label htmlFor="postWish">
|
||||
Envies:
|
||||
<textarea
|
||||
id="postEnvies"
|
||||
name="postEnvies"
|
||||
value={envies}
|
||||
onChange={onEnviesChanged}
|
||||
/>
|
||||
<textarea id="postWish" name="postWish" value={wish} onChange={onWishChanged} />
|
||||
</label>
|
||||
<label htmlFor="postPrecisions">
|
||||
<label htmlFor="postDetails">
|
||||
Precisions:
|
||||
<textarea
|
||||
id="postPrecisions"
|
||||
name="postPrecisions"
|
||||
value={precisions}
|
||||
onChange={onPrecisionsChanged}
|
||||
id="postDetails"
|
||||
name="postDetails"
|
||||
value={details}
|
||||
onChange={onDetailsChanged}
|
||||
/>
|
||||
</label>
|
||||
<label htmlFor="postEquipes">
|
||||
<label htmlFor="postTeams">
|
||||
Equipes:
|
||||
<input
|
||||
type="text"
|
||||
id="postEquipes"
|
||||
name="postEquipes"
|
||||
value={equipes.join(", ")}
|
||||
onChange={onEquipesChanged}
|
||||
id="postTeams"
|
||||
name="postTeams"
|
||||
value={teams.join(", ")}
|
||||
onChange={onTeamsChanged}
|
||||
/>
|
||||
</label>
|
||||
<label htmlFor="postDateAjout">
|
||||
DateAjout:
|
||||
<label htmlFor="postAddedDate">
|
||||
Date dajout:
|
||||
<input
|
||||
type="date"
|
||||
id="postDateAjout"
|
||||
name="postDateAjout"
|
||||
value={dateAjout}
|
||||
onChange={onDateAjoutChanged}
|
||||
id="postAddedDate"
|
||||
name="postAddedDate"
|
||||
value={addedDate}
|
||||
onChange={onAddedDateChanged}
|
||||
/>
|
||||
</label>
|
||||
<button type="button" onClick={onSavePostClicked}>
|
||||
|
@ -28,21 +28,20 @@ describe("<List />", () => {
|
||||
entities: {
|
||||
"5": {
|
||||
id: 5,
|
||||
titre: "6 qui prend!",
|
||||
auteur: "Wolfgang Kramer",
|
||||
editeur: "(uncredited) , Design Edge , B",
|
||||
minJoueurs: 2,
|
||||
maxJoueurs: 10,
|
||||
duree: 45,
|
||||
title: "6 qui prend!",
|
||||
author: "Wolfgang Kramer",
|
||||
editor: "(uncredited) , Design Edge , B",
|
||||
playersMin: 2,
|
||||
playersMax: 10,
|
||||
duration: 45,
|
||||
type: "Ambiance",
|
||||
poufpaf: "0-9-2/6-qui-prend-6-nimmt",
|
||||
photo: "https://cf.geekdo-images.com/thumb/img/lzczxR5cw7an7tRWeHdOrRtLyes=/fit-in/200x150/pic772547.jpg",
|
||||
bggPhoto: "",
|
||||
bggPhoto:
|
||||
"https://cf.geekdo-images.com/thumb/img/lzczxR5cw7an7tRWeHdOrRtLyes=/fit-in/200x150/pic772547.jpg",
|
||||
bggId: 432,
|
||||
exemplaires: 1,
|
||||
dispoPret: 1,
|
||||
nonRangee: 0,
|
||||
horodatage: "0000-00-00",
|
||||
copies: 1,
|
||||
lendAvailability: 1,
|
||||
notStored: 0,
|
||||
ean: "3421272101313",
|
||||
},
|
||||
},
|
||||
|
@ -21,10 +21,10 @@ const JeuJavList = ({ ids }: Props) => {
|
||||
if (!jeu) {
|
||||
return <li key={id}>Le jeu #{id} n'existe pas</li>
|
||||
}
|
||||
const { titre, bggId } = jeu
|
||||
const { title, bggId } = jeu
|
||||
return (
|
||||
<li key={id}>
|
||||
{titre} - [{bggId}]
|
||||
{title} - [{bggId}]
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
|
@ -13,18 +13,18 @@ describe("<MembreInfo />", () => {
|
||||
<MembreInfo
|
||||
item={{
|
||||
id: 1,
|
||||
nom: "Aupeix",
|
||||
prenom: "Amélie",
|
||||
mail: "pakouille.lakouille@yahoo.fr",
|
||||
telephone: "0675650392",
|
||||
firstname: "Aupeix",
|
||||
lastname: "Amélie",
|
||||
email: "pakouille.lakouille@yahoo.fr",
|
||||
mobile: "0675650392",
|
||||
photo: "images/membres/$taille/amélie_aupeix.jpg",
|
||||
alimentation: "Végétarien",
|
||||
majeur: 1,
|
||||
privilege: 0,
|
||||
actif: 0,
|
||||
commentaire: "",
|
||||
horodatage: "0000-00-00",
|
||||
passe: "$2y$10$fSxY9AIuxSiEjwF.J3eXGubIxUPlobkyRrNIal8ASimSjNj4SR.9O",
|
||||
food: "Végétarien",
|
||||
adult: 1,
|
||||
privileges: 0,
|
||||
active: 0,
|
||||
comment: "",
|
||||
timestamp: "0000-00-00",
|
||||
password: "$2y$10$fSxY9AIuxSiEjwF.J3eXGubIxUPkdq9d5fqpbl8ASimSjNj4SR.9O",
|
||||
}}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
|
@ -10,11 +10,11 @@ exports[`<MembreInfo /> renders 1`] = `
|
||||
<ul>
|
||||
<li>
|
||||
Prénom:
|
||||
Amélie
|
||||
Aupeix
|
||||
</li>
|
||||
<li>
|
||||
Nom:
|
||||
Aupeix
|
||||
Amélie
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -11,8 +11,8 @@ const MembreInfo = ({ item }: Props) => (
|
||||
<div className={styles.MembreCard}>
|
||||
<h4>Membre Info</h4>
|
||||
<ul>
|
||||
<li>Prénom: {item.prenom}</li>
|
||||
<li>Nom: {item.nom}</li>
|
||||
<li>Prénom: {item.firstname}</li>
|
||||
<li>Nom: {item.lastname}</li>
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
|
@ -14,18 +14,19 @@ describe("<MembreList />", () => {
|
||||
items={[
|
||||
{
|
||||
id: 1,
|
||||
nom: "Aupeix",
|
||||
prenom: "Amélie",
|
||||
mail: "pakouille.lakouille@yahoo.fr",
|
||||
telephone: "0675650392",
|
||||
firstname: "Aupeix",
|
||||
lastname: "Amélie",
|
||||
email: "pakouille.lakouille@yahoo.fr",
|
||||
mobile: "0675650392",
|
||||
photo: "images/membres/$taille/amélie_aupeix.jpg",
|
||||
alimentation: "Végétarien",
|
||||
majeur: 1,
|
||||
privilege: 0,
|
||||
actif: 0,
|
||||
commentaire: "",
|
||||
horodatage: "0000-00-00",
|
||||
passe: "$2y$10$fSxY9AIuxSiEjwF.J3eXGubIxUPlobkyRrNIal8ASimSjNj4SR.9O",
|
||||
food: "Végétarien",
|
||||
adult: 1,
|
||||
privileges: 0,
|
||||
active: 0,
|
||||
comment: "",
|
||||
timestamp: "0000-00-00",
|
||||
password:
|
||||
"$2y$10$fSxY9AIuxSiEjwF.J3eXGubIxUPkdq9d5fqpbl8ASimSjNj4SR.9O",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
@ -13,10 +13,10 @@ exports[`<MembreList /> renders 1`] = `
|
||||
href="/Membre/1"
|
||||
>
|
||||
<b>
|
||||
Amélie
|
||||
Aupeix
|
||||
</b>
|
||||
|
||||
Aupeix
|
||||
Amélie
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -12,10 +12,10 @@ const MembreList = ({ items }: Props) => (
|
||||
<div className={styles["user-list"]}>
|
||||
<h4>Membre List</h4>
|
||||
<ul>
|
||||
{items.map(({ id, nom, prenom }) => (
|
||||
{items.map(({ id, lastname, firstname }) => (
|
||||
<li key={id}>
|
||||
<Link to={`/Membre/${id}`}>
|
||||
<b>{prenom}</b> {nom}
|
||||
<b>{firstname}</b> {lastname}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
|
@ -15,18 +15,18 @@ describe("<SetMembre />", () => {
|
||||
dispatch={dispatch}
|
||||
membre={{
|
||||
id: 1,
|
||||
nom: "Aupeix",
|
||||
prenom: "Amélie",
|
||||
mail: "pakouille.lakouille@yahoo.fr",
|
||||
telephone: "0675650392",
|
||||
firstname: "Aupeix",
|
||||
lastname: "Amélie",
|
||||
email: "pakouille.lakouille@yahoo.fr",
|
||||
mobile: "0675650392",
|
||||
photo: "images/membres/$taille/amélie_aupeix.jpg",
|
||||
alimentation: "Végétarien",
|
||||
majeur: 1,
|
||||
privilege: 0,
|
||||
actif: 0,
|
||||
commentaire: "",
|
||||
horodatage: "0000-00-00",
|
||||
passe: "$2y$10$fSxY9AIuxSiEjwF.J3eXGubIxUPlobkyRrNIal8ASimSjNj4SR.9O",
|
||||
food: "Végétarien",
|
||||
adult: 1,
|
||||
privileges: 0,
|
||||
active: 0,
|
||||
comment: "",
|
||||
timestamp: "0000-00-00",
|
||||
password: "$2y$10$fSxY9AIuxSiEjwF.J3eXGubIxUPkdq9d5fqpbl8ASimSjNj4SR.9O",
|
||||
}}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
|
@ -9,34 +9,34 @@ exports[`<SetMembre /> renders 1`] = `
|
||||
</h2>
|
||||
<form>
|
||||
<label
|
||||
for="postPrenom"
|
||||
for="postFirstname"
|
||||
>
|
||||
Prenom:
|
||||
Prénom:
|
||||
<input
|
||||
id="postPrenom"
|
||||
name="postPrenom"
|
||||
type="text"
|
||||
value="Amélie"
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postNom"
|
||||
>
|
||||
Nom:
|
||||
<input
|
||||
id="postNom"
|
||||
name="postNom"
|
||||
id="postFirstname"
|
||||
name="postFirstname"
|
||||
type="text"
|
||||
value="Aupeix"
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postMajeur"
|
||||
for="postName"
|
||||
>
|
||||
Nom:
|
||||
<input
|
||||
id="postName"
|
||||
name="postName"
|
||||
type="text"
|
||||
value="Amélie"
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postAdult"
|
||||
>
|
||||
Majeur:
|
||||
<input
|
||||
id="postMajeur"
|
||||
name="postMajeur"
|
||||
id="postAdult"
|
||||
name="postAdult"
|
||||
type="text"
|
||||
value="1"
|
||||
/>
|
||||
|
@ -13,22 +13,23 @@ interface Props {
|
||||
}
|
||||
|
||||
const MembreSet = ({ dispatch, membre }: Props) => {
|
||||
const [prenom, setPrenom] = useState(membre.prenom)
|
||||
const [nom, setNom] = useState(membre.nom)
|
||||
const [majeur, setMajeur] = useState(membre.majeur)
|
||||
const [firstname, setFirstname] = useState(membre.firstname)
|
||||
const [lastname, setName] = useState(membre.lastname)
|
||||
const [adult, setAdult] = useState(membre.adult)
|
||||
|
||||
const onPrenomChanged = (e: React.ChangeEvent<HTMLInputElement>) => setPrenom(e.target.value)
|
||||
const onNomChanged = (e: React.ChangeEvent<HTMLInputElement>) => setNom(e.target.value)
|
||||
const onMajeurChanged = (e: React.ChangeEvent<HTMLInputElement>) => setMajeur(+e.target.value)
|
||||
const onFirstnameChanged = (e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setFirstname(e.target.value)
|
||||
const onNameChanged = (e: React.ChangeEvent<HTMLInputElement>) => setName(e.target.value)
|
||||
const onAdultChanged = (e: React.ChangeEvent<HTMLInputElement>) => setAdult(+e.target.value)
|
||||
|
||||
const onSavePostClicked = () => {
|
||||
if (prenom && nom) {
|
||||
if (firstname && lastname) {
|
||||
dispatch(
|
||||
fetchMembreSet({
|
||||
...membre,
|
||||
prenom,
|
||||
nom,
|
||||
majeur,
|
||||
firstname,
|
||||
lastname,
|
||||
adult,
|
||||
})
|
||||
)
|
||||
} else {
|
||||
@ -47,34 +48,34 @@ const MembreSet = ({ dispatch, membre }: Props) => {
|
||||
<section className={styles.MembreList}>
|
||||
<h2>Modifier un membre</h2>
|
||||
<form>
|
||||
<label htmlFor="postPrenom">
|
||||
Prenom:
|
||||
<label htmlFor="postFirstname">
|
||||
Prénom:
|
||||
<input
|
||||
type="text"
|
||||
id="postPrenom"
|
||||
name="postPrenom"
|
||||
value={prenom}
|
||||
onChange={onPrenomChanged}
|
||||
id="postFirstname"
|
||||
name="postFirstname"
|
||||
value={firstname}
|
||||
onChange={onFirstnameChanged}
|
||||
/>
|
||||
</label>
|
||||
<label htmlFor="postNom">
|
||||
<label htmlFor="postName">
|
||||
Nom:
|
||||
<input
|
||||
type="text"
|
||||
id="postNom"
|
||||
name="postNom"
|
||||
value={nom}
|
||||
onChange={onNomChanged}
|
||||
id="postName"
|
||||
name="postName"
|
||||
value={lastname}
|
||||
onChange={onNameChanged}
|
||||
/>
|
||||
</label>
|
||||
<label htmlFor="postMajeur">
|
||||
<label htmlFor="postAdult">
|
||||
Majeur:
|
||||
<input
|
||||
type="text"
|
||||
id="postMajeur"
|
||||
name="postMajeur"
|
||||
value={majeur}
|
||||
onChange={onMajeurChanged}
|
||||
id="postAdult"
|
||||
name="postAdult"
|
||||
value={adult}
|
||||
onChange={onAdultChanged}
|
||||
/>
|
||||
</label>
|
||||
<button type="button" onClick={onSavePostClicked}>
|
||||
|
@ -1,28 +1,93 @@
|
||||
import React, { memo, useCallback } from "react"
|
||||
import React, { memo, useState } from "react"
|
||||
import { useSelector, shallowEqual } from "react-redux"
|
||||
import { toast } from "react-toastify"
|
||||
import _ from "lodash"
|
||||
import styles from "./styles.module.scss"
|
||||
|
||||
const RegisterForm = (): JSX.Element => {
|
||||
const onSubmit = useCallback((event: React.SyntheticEvent): void => {
|
||||
event.preventDefault()
|
||||
const target = event.target as typeof event.target & {
|
||||
firstname: { value: string }
|
||||
lastname: { value: string }
|
||||
email: { value: string }
|
||||
phone: { value: string }
|
||||
import { fetchPreMemberAdd } from "../../store/preMemberAdd"
|
||||
import { AppDispatch, AppState } from "../../store"
|
||||
|
||||
interface Props {
|
||||
dispatch: AppDispatch
|
||||
}
|
||||
|
||||
const RegisterForm = ({ dispatch }: Props): JSX.Element => {
|
||||
const [firstname, setFirstname] = useState("")
|
||||
const [lastname, setLastname] = useState("")
|
||||
const [email, setEmail] = useState("")
|
||||
const [mobile, setMobile] = useState("")
|
||||
const [alreadyVolunteer, setAlreadyVolunteer] = useState(false)
|
||||
const [comment, setComment] = useState("")
|
||||
const [sending, setSending] = useState(false)
|
||||
|
||||
const onFirstnameChanged = (e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setFirstname(e.target.value)
|
||||
const onLastnameChanged = (e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setLastname(e.target.value)
|
||||
const onEmailChanged = (e: React.ChangeEvent<HTMLInputElement>) => setEmail(e.target.value)
|
||||
const onMobileChanged = (e: React.ChangeEvent<HTMLInputElement>) => setMobile(e.target.value)
|
||||
const onAlreadyVolunteer = (e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setAlreadyVolunteer(!!e.target.value)
|
||||
const onNotYesVolunteer = (e: React.ChangeEvent<HTMLInputElement>) =>
|
||||
setAlreadyVolunteer(!e.target.value)
|
||||
const onCommentChanged = (e: React.ChangeEvent<HTMLTextAreaElement>) =>
|
||||
setComment(e.target.value)
|
||||
|
||||
const onSubmit = () => {
|
||||
if (firstname && lastname && email && mobile && !sending) {
|
||||
dispatch(
|
||||
fetchPreMemberAdd({
|
||||
firstname,
|
||||
lastname,
|
||||
email,
|
||||
mobile,
|
||||
alreadyVolunteer,
|
||||
comment,
|
||||
})
|
||||
)
|
||||
|
||||
setSending(true)
|
||||
} else {
|
||||
toast.warning("Il faut remplir tous les champs (sauf le dernier)", {
|
||||
position: "top-center",
|
||||
autoClose: 6000,
|
||||
hideProgressBar: true,
|
||||
closeOnClick: true,
|
||||
pauseOnHover: true,
|
||||
draggable: true,
|
||||
progress: undefined,
|
||||
})
|
||||
}
|
||||
const firstname = target.firstname.value
|
||||
const lastname = target.lastname.value
|
||||
const email = target.email.value
|
||||
const phone = target.phone.value
|
||||
}
|
||||
|
||||
console.log("register fields checked", firstname, lastname, email, phone)
|
||||
const { error, entities: preMember } = useSelector(
|
||||
(state: AppState) => state.preMemberAdd,
|
||||
shallowEqual
|
||||
)
|
||||
|
||||
// call service with fields
|
||||
}, [])
|
||||
let sendSuccess
|
||||
if (!_.isEmpty(preMember)) {
|
||||
if (sending) {
|
||||
setSending(false)
|
||||
}
|
||||
sendSuccess = <span className={styles.success}>Formulaire envoyé !</span>
|
||||
}
|
||||
|
||||
let sendError
|
||||
if (error && _.isEmpty(preMember)) {
|
||||
if (sending) {
|
||||
setSending(false)
|
||||
}
|
||||
sendError = <span className={styles.error}>{error}</span>
|
||||
}
|
||||
|
||||
let sendingElement
|
||||
if (sending) {
|
||||
sendingElement = <span className={styles.sending}>Envoi en cours...</span>
|
||||
}
|
||||
/*
|
||||
prenom
|
||||
nom
|
||||
firstname
|
||||
lastname
|
||||
mail
|
||||
tel
|
||||
j'ai déjà été bénévole pour PEL
|
||||
@ -35,8 +100,8 @@ const RegisterForm = (): JSX.Element => {
|
||||
<dt>Qu'est-ce que Paris est Ludique ?</dt>
|
||||
<dd>
|
||||
<p>
|
||||
Cette grande fête est dédiée aux <b>jeux de société modernes</b> sous toutes
|
||||
leurs formes.
|
||||
Un festival en plein air dédiée aux <b>jeux de société modernes</b> sous
|
||||
toutes leurs formes.
|
||||
</p>
|
||||
<p>
|
||||
En 2019 lors de la dernière édition, ce sont <b>16 000</b> joueurs qui se
|
||||
@ -53,60 +118,88 @@ const RegisterForm = (): JSX.Element => {
|
||||
<dd>
|
||||
<p>
|
||||
L'organisation du festival est <b>entièrement gérée par nous</b>, les
|
||||
bénévoles. À aucun moment ça ne doit devenir une corvée, donc nous faisons
|
||||
tout pour passer <b>un aussi bon moment que les visiteurs</b> :)
|
||||
bénévoles. À aucun moment ça ne ressemble à du travail : nous faisons tout
|
||||
pour passer <b>un aussi bon moment que les visiteurs</b> :)
|
||||
</p>
|
||||
<p>
|
||||
C'est pour ça que chaque mois, ceux qui sont dispo prennent
|
||||
l'apéro tous ensemble en jouant et discutant de l'organisation.
|
||||
D'ailleurs, un soir par mois nous nous réunissons pour un apéro ludique
|
||||
où discuter de l'organisation ! On joue autant que les visiteurs, mais
|
||||
sur toute l'année ^^
|
||||
</p>
|
||||
<p>
|
||||
Pendant le festival de 2019, nous étions <b>187 bénévoles</b> organisés en
|
||||
équipes spécialisées qui chouchoutent les visiteurs en les accueillant, en
|
||||
s'assurant que tout se passe bien, ou en expliquant des règles de jeux.
|
||||
équipes qui chouchoutent les visiteurs en les accueillant, en
|
||||
s'assurant que tout se passe bien, ou encore en expliquant des règles
|
||||
de jeux.
|
||||
</p>
|
||||
<p>
|
||||
Une équipe s'occupe même du bien être des bénévoles en leur servant à
|
||||
boire et à manger dans un espace à part où faire des pauses régulières.
|
||||
Une équipe est même dédiée au bien être des bénévoles en leur servant à
|
||||
boire et à manger dans un espace à part où faire des pauses régulières. Et
|
||||
puis nous hébergeons ceux d'entre nous qui habitent loin de Paris. Le
|
||||
confort avant tout !
|
||||
</p>
|
||||
<p>
|
||||
Les deux jours avant et le jour après le festival, ceux qui le peuvent
|
||||
viennent tout préparer et ranger. Certains ne sont disponibles que ces jours
|
||||
là et c'est déjà d'une grande aide !
|
||||
Certains bénévoles sont visiteurs le samedi ou le dimanche pour vivre le
|
||||
festival de l'intérieur. Les deux jours avant et le jour après le
|
||||
festival, ceux qui le peuvent viennent préparer et ranger. Bref, chacun
|
||||
participe à la hauteur de ses envies et disponibilités !
|
||||
</p>
|
||||
<p>
|
||||
Nous nous arrangeons pour héberger les bénévoles qui habitent loin de Paris,
|
||||
et certains ne viennent qu'un seul jour du weekend pour être visiteur
|
||||
l'autre.
|
||||
</p>
|
||||
<p>
|
||||
Le samedi soir, cerise sur le gâteau, nous prenons un{" "}
|
||||
<b>dîner avec les auteurs, illustrateurs et éditeurs</b> qui sont présents
|
||||
sur le festival !
|
||||
Le samedi soir quand les visiteurs sont partis, nous prolongeons la fête en
|
||||
dînant avec les auteurs, illustrateurs et éditeurs présents sur le festival.
|
||||
</p>
|
||||
</dd>
|
||||
<dt>
|
||||
Si l'expérience vous tente, n'hésitez pas à remplir le formulaire
|
||||
suivant pour nous rencontrer lors d'un des gros apéros mensuels !<br />
|
||||
Si l'expérience pourrait vous tenter, remplissez le formulaire suivant pour
|
||||
en discuter lors d'un des gros apéros mensuels !<br />
|
||||
Cette inscription ne vous oblige en rien il s'agit juste d'une prise
|
||||
de contact.
|
||||
<br />
|
||||
Les prochains sont les 21 décembre et 27 janvier, mais nous vous appelerons
|
||||
d'ici là pour discuter :)
|
||||
d'ici là pour les détails :)
|
||||
<br />
|
||||
<span className={styles.lightTitle}>(Déjà au moins 8 inscrits !)</span>
|
||||
</dt>
|
||||
<dd>
|
||||
<div className={styles.formLine} key="line-firstname">
|
||||
<label htmlFor="firstname">Prénom</label>
|
||||
<input type="text" id="firstname" />
|
||||
<input
|
||||
type="text"
|
||||
id="firstname"
|
||||
required
|
||||
value={firstname}
|
||||
onChange={onFirstnameChanged}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.formLine} key="line-lastname">
|
||||
<label htmlFor="lastname">Nom</label>
|
||||
<input type="text" id="lastname" />
|
||||
<input
|
||||
type="text"
|
||||
id="lastname"
|
||||
required
|
||||
value={lastname}
|
||||
onChange={onLastnameChanged}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.formLine} key="line-email">
|
||||
<label htmlFor="email">Email</label>
|
||||
<input type="email" id="email" />
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
required
|
||||
value={email}
|
||||
onChange={onEmailChanged}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.formLine} key="line-phone">
|
||||
<label htmlFor="phone">Téléphone</label>
|
||||
<input type="text" id="phone" />
|
||||
<div className={styles.formLine} key="line-mobile">
|
||||
<label htmlFor="mobile">Téléphone</label>
|
||||
<input
|
||||
type="text"
|
||||
id="mobile"
|
||||
required
|
||||
value={mobile}
|
||||
onChange={onMobileChanged}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.formLine} key="line-already-volunteer">
|
||||
<div>
|
||||
@ -116,6 +209,8 @@ const RegisterForm = (): JSX.Element => {
|
||||
name="alreadyVolunteer"
|
||||
id="alreadyVolunteer-yes"
|
||||
className={styles.inputRadio}
|
||||
checked={alreadyVolunteer}
|
||||
onChange={onAlreadyVolunteer}
|
||||
/>
|
||||
<label htmlFor="alreadyVolunteer-yes">Oui</label>
|
||||
<input
|
||||
@ -123,6 +218,8 @@ const RegisterForm = (): JSX.Element => {
|
||||
name="alreadyVolunteer"
|
||||
id="alreadyVolunteer-no"
|
||||
className={styles.inputRadio}
|
||||
checked={!alreadyVolunteer}
|
||||
onChange={onNotYesVolunteer}
|
||||
/>
|
||||
<label htmlFor="alreadyVolunteer-no">Non</label>
|
||||
</div>
|
||||
@ -132,10 +229,19 @@ const RegisterForm = (): JSX.Element => {
|
||||
name="message"
|
||||
id="message"
|
||||
placeholder="Des petits mots sympas, questions, envies, des infos sur toi, des compétences dont tu aimerais te servir... ou rien de tout ça et nous en discuterons au téléphone :)"
|
||||
value={comment}
|
||||
onChange={onCommentChanged}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.formButtons}>
|
||||
<button type="submit">Envoyer</button>
|
||||
<button type="button" onClick={onSubmit} disabled={sending}>
|
||||
Envoyer
|
||||
</button>
|
||||
</div>
|
||||
<div className={styles.formReactions}>
|
||||
{sendingElement}
|
||||
{sendSuccess}
|
||||
{sendError}
|
||||
</div>
|
||||
</dd>
|
||||
</dl>
|
||||
|
@ -16,6 +16,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.lightTitle {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.formLine {
|
||||
padding: 5px 0;
|
||||
|
||||
@ -48,4 +52,25 @@
|
||||
margin-top: 10px;
|
||||
padding: 5px 0;
|
||||
text-align: center;
|
||||
|
||||
[disabled="true"] {
|
||||
background-color: #333333c0;
|
||||
color: #cccccce7;
|
||||
}
|
||||
}
|
||||
|
||||
.formReactions {
|
||||
margin-top: 3px;
|
||||
padding: 5px 0;
|
||||
text-align: center;
|
||||
|
||||
.sending {
|
||||
color: rgb(0, 0, 255);
|
||||
}
|
||||
.success {
|
||||
color: rgb(0, 133, 0);
|
||||
}
|
||||
.error {
|
||||
color: rgb(255, 0, 0);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
const PORT = 4000
|
||||
const API_URL = __DEV__ || __LOCAL__ ? `http://localhost:${PORT}` : "https://fo.parisestludique.fr"
|
||||
const PROTOCOL = (typeof window !== "undefined" && window?.location?.protocol) || "http:"
|
||||
const PORT = 4000 + (PROTOCOL === "https:" ? 2 : 0)
|
||||
const API_URL =
|
||||
__DEV__ || __LOCAL__ ? `${PROTOCOL}//localhost:${PORT}` : `${PROTOCOL}//fo.parisestludique.fr`
|
||||
|
||||
export default {
|
||||
PORT,
|
||||
|
@ -1,79 +0,0 @@
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
import { render } from "@testing-library/react"
|
||||
import { MemoryRouter } from "react-router-dom"
|
||||
|
||||
import { fetchJeuJavListIfNeed } from "../../../store/jeuJavList"
|
||||
import mockStore from "../../../utils/mockStore"
|
||||
import Home from "../Home"
|
||||
|
||||
describe("<Home />", () => {
|
||||
const renderHelper = (reducer = { readyStatus: "idle" }) => {
|
||||
const { dispatch, ProviderWithStore } = mockStore({ jeuJavList: reducer })
|
||||
const { container } = render(
|
||||
<ProviderWithStore>
|
||||
<MemoryRouter>
|
||||
{/*
|
||||
@ts-expect-error */}
|
||||
<Home />
|
||||
</MemoryRouter>
|
||||
</ProviderWithStore>
|
||||
)
|
||||
|
||||
return { dispatch, firstChild: container.firstChild }
|
||||
}
|
||||
|
||||
it("should fetch data when page loaded", () => {
|
||||
const { dispatch } = renderHelper()
|
||||
|
||||
expect(dispatch).toHaveBeenCalledTimes(1)
|
||||
expect(dispatch.mock.calls[0][0].toString()).toBe(fetchJeuJavListIfNeed().toString())
|
||||
})
|
||||
|
||||
it("renders the loading status if data invalid", () => {
|
||||
expect(renderHelper().firstChild).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it("renders the loading status if requesting data", () => {
|
||||
const reducer = { readyStatus: "request" }
|
||||
|
||||
expect(renderHelper(reducer).firstChild).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it("renders an error if loading failed", () => {
|
||||
const reducer = { readyStatus: "failure" }
|
||||
|
||||
expect(renderHelper(reducer).firstChild).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it("renders the <List /> if loading was successful", () => {
|
||||
const reducer = {
|
||||
readyStatus: "success",
|
||||
ids: [5],
|
||||
entities: {
|
||||
"5": {
|
||||
id: 5,
|
||||
titre: "6 qui prend!",
|
||||
auteur: "Wolfgang Kramer",
|
||||
editeur: "(uncredited) , Design Edge , B",
|
||||
minJoueurs: 2,
|
||||
maxJoueurs: 10,
|
||||
duree: 45,
|
||||
type: "Ambiance",
|
||||
poufpaf: "0-9-2/6-qui-prend-6-nimmt",
|
||||
photo: "https://cf.geekdo-images.com/thumb/img/lzczxR5cw7an7tRWeHdOrRtLyes=/fit-in/200x150/pic772547.jpg",
|
||||
bggPhoto: "",
|
||||
bggId: 432,
|
||||
exemplaires: 1,
|
||||
dispoPret: 1,
|
||||
nonRangee: 0,
|
||||
horodatage: "0000-00-00",
|
||||
ean: "3421272101313",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expect(renderHelper(reducer).firstChild).toMatchSnapshot()
|
||||
})
|
||||
})
|
@ -1,313 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<Home /> renders an error if loading failed 1`] = `
|
||||
<div
|
||||
class="home"
|
||||
>
|
||||
<section
|
||||
class="EnvieList"
|
||||
>
|
||||
<h2>
|
||||
Ajouter une nouvelle envie
|
||||
</h2>
|
||||
<form>
|
||||
<label
|
||||
for="postDomaine"
|
||||
>
|
||||
Domaine:
|
||||
<input
|
||||
id="postDomaine"
|
||||
name="postDomaine"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postEnvies"
|
||||
>
|
||||
Envies:
|
||||
<textarea
|
||||
id="postEnvies"
|
||||
name="postEnvies"
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postPrecisions"
|
||||
>
|
||||
Precisions:
|
||||
<textarea
|
||||
id="postPrecisions"
|
||||
name="postPrecisions"
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postEquipes"
|
||||
>
|
||||
Equipes:
|
||||
<input
|
||||
id="postEquipes"
|
||||
name="postEquipes"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postDateAjout"
|
||||
>
|
||||
DateAjout:
|
||||
<input
|
||||
id="postDateAjout"
|
||||
name="postDateAjout"
|
||||
type="date"
|
||||
value=""
|
||||
/>
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
>
|
||||
Save Post
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
<p>
|
||||
Oops, Failed to load list!
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<Home /> renders the <List /> if loading was successful 1`] = `
|
||||
<div
|
||||
class="home"
|
||||
>
|
||||
<section
|
||||
class="EnvieList"
|
||||
>
|
||||
<h2>
|
||||
Ajouter une nouvelle envie
|
||||
</h2>
|
||||
<form>
|
||||
<label
|
||||
for="postDomaine"
|
||||
>
|
||||
Domaine:
|
||||
<input
|
||||
id="postDomaine"
|
||||
name="postDomaine"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postEnvies"
|
||||
>
|
||||
Envies:
|
||||
<textarea
|
||||
id="postEnvies"
|
||||
name="postEnvies"
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postPrecisions"
|
||||
>
|
||||
Precisions:
|
||||
<textarea
|
||||
id="postPrecisions"
|
||||
name="postPrecisions"
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postEquipes"
|
||||
>
|
||||
Equipes:
|
||||
<input
|
||||
id="postEquipes"
|
||||
name="postEquipes"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postDateAjout"
|
||||
>
|
||||
DateAjout:
|
||||
<input
|
||||
id="postDateAjout"
|
||||
name="postDateAjout"
|
||||
type="date"
|
||||
value=""
|
||||
/>
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
>
|
||||
Save Post
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
<div
|
||||
class="JeuJavList"
|
||||
>
|
||||
<h4>
|
||||
Jeux JAV
|
||||
</h4>
|
||||
<ul>
|
||||
<li>
|
||||
6 qui prend!
|
||||
- [
|
||||
432
|
||||
]
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<Home /> renders the loading status if data invalid 1`] = `
|
||||
<div
|
||||
class="home"
|
||||
>
|
||||
<section
|
||||
class="EnvieList"
|
||||
>
|
||||
<h2>
|
||||
Ajouter une nouvelle envie
|
||||
</h2>
|
||||
<form>
|
||||
<label
|
||||
for="postDomaine"
|
||||
>
|
||||
Domaine:
|
||||
<input
|
||||
id="postDomaine"
|
||||
name="postDomaine"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postEnvies"
|
||||
>
|
||||
Envies:
|
||||
<textarea
|
||||
id="postEnvies"
|
||||
name="postEnvies"
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postPrecisions"
|
||||
>
|
||||
Precisions:
|
||||
<textarea
|
||||
id="postPrecisions"
|
||||
name="postPrecisions"
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postEquipes"
|
||||
>
|
||||
Equipes:
|
||||
<input
|
||||
id="postEquipes"
|
||||
name="postEquipes"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postDateAjout"
|
||||
>
|
||||
DateAjout:
|
||||
<input
|
||||
id="postDateAjout"
|
||||
name="postDateAjout"
|
||||
type="date"
|
||||
value=""
|
||||
/>
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
>
|
||||
Save Post
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
<p>
|
||||
Loading...
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<Home /> renders the loading status if requesting data 1`] = `
|
||||
<div
|
||||
class="home"
|
||||
>
|
||||
<section
|
||||
class="EnvieList"
|
||||
>
|
||||
<h2>
|
||||
Ajouter une nouvelle envie
|
||||
</h2>
|
||||
<form>
|
||||
<label
|
||||
for="postDomaine"
|
||||
>
|
||||
Domaine:
|
||||
<input
|
||||
id="postDomaine"
|
||||
name="postDomaine"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postEnvies"
|
||||
>
|
||||
Envies:
|
||||
<textarea
|
||||
id="postEnvies"
|
||||
name="postEnvies"
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postPrecisions"
|
||||
>
|
||||
Precisions:
|
||||
<textarea
|
||||
id="postPrecisions"
|
||||
name="postPrecisions"
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postEquipes"
|
||||
>
|
||||
Equipes:
|
||||
<input
|
||||
id="postEquipes"
|
||||
name="postEquipes"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postDateAjout"
|
||||
>
|
||||
DateAjout:
|
||||
<input
|
||||
id="postDateAjout"
|
||||
name="postDateAjout"
|
||||
type="date"
|
||||
value=""
|
||||
/>
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
>
|
||||
Save Post
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
<p>
|
||||
Loading...
|
||||
</p>
|
||||
</div>
|
||||
`;
|
@ -1,72 +0,0 @@
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
import { render } from "@testing-library/react"
|
||||
import { MemoryRouter } from "react-router-dom"
|
||||
|
||||
import { fetchMembreIfNeed } from "../../../store/membre"
|
||||
import mockStore from "../../../utils/mockStore"
|
||||
import MembrePage from "../MembrePage"
|
||||
|
||||
describe("<MembrePage />", () => {
|
||||
const mockData = {
|
||||
id: 1,
|
||||
nom: "Aupeix",
|
||||
prenom: "Amélie",
|
||||
mail: "pakouille.lakouille@yahoo.fr",
|
||||
telephone: "0675650392",
|
||||
photo: "images/membres/$taille/amélie_aupeix.jpg",
|
||||
alimentation: "Végétarien",
|
||||
majeur: 1,
|
||||
privilege: 0,
|
||||
actif: 0,
|
||||
commentaire: "",
|
||||
horodatage: "0000-00-00",
|
||||
passe: "$2y$10$fSxY9AIuxSiEjwF.J3eXGubIxUPlobkyRrNIal8ASimSjNj4SR.9O",
|
||||
}
|
||||
const { id } = mockData
|
||||
|
||||
const renderHelper = (reducer = {}) => {
|
||||
const { dispatch, ProviderWithStore } = mockStore({ membre: reducer })
|
||||
const { container } = render(
|
||||
<ProviderWithStore>
|
||||
<MemoryRouter>
|
||||
{/*
|
||||
@ts-expect-error */}
|
||||
<MembrePage match={{ params: { id } }} />
|
||||
</MemoryRouter>
|
||||
</ProviderWithStore>
|
||||
)
|
||||
|
||||
return { dispatch, firstChild: container.firstChild }
|
||||
}
|
||||
|
||||
it("should fetch data when page loaded", () => {
|
||||
const { dispatch } = renderHelper()
|
||||
|
||||
expect(dispatch).toHaveBeenCalledTimes(1)
|
||||
expect(dispatch.mock.calls[0][0].toString()).toBe(fetchMembreIfNeed(id).toString())
|
||||
})
|
||||
|
||||
it("renders the loading status if data invalid", () => {
|
||||
expect(renderHelper().firstChild).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it("renders the loading status if requesting data", () => {
|
||||
const reducer = { readyStatus: "request" }
|
||||
|
||||
expect(renderHelper(reducer).firstChild).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it("renders an error if loading failed", () => {
|
||||
const reducer = { readyStatus: "failure" }
|
||||
|
||||
expect(renderHelper(reducer).firstChild).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it("renders the <Info /> if loading was successful", () => {
|
||||
const reducer = { readyStatus: "success", entity: mockData }
|
||||
|
||||
expect(renderHelper(reducer).firstChild).toMatchSnapshot()
|
||||
})
|
||||
})
|
@ -1,104 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<MembrePage /> renders an error if loading failed 1`] = `
|
||||
<div
|
||||
class="membre"
|
||||
>
|
||||
<p>
|
||||
Oops! Failed to load data.
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<MembrePage /> renders the <Info /> if loading was successful 1`] = `
|
||||
<div
|
||||
class="membre"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
class="MembreCard"
|
||||
>
|
||||
<h4>
|
||||
Membre Info
|
||||
</h4>
|
||||
<ul>
|
||||
<li>
|
||||
Prénom:
|
||||
Amélie
|
||||
</li>
|
||||
<li>
|
||||
Nom:
|
||||
Aupeix
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<section
|
||||
class="MembreList"
|
||||
>
|
||||
<h2>
|
||||
Modifier un membre
|
||||
</h2>
|
||||
<form>
|
||||
<label
|
||||
for="postPrenom"
|
||||
>
|
||||
Prenom:
|
||||
<input
|
||||
id="postPrenom"
|
||||
name="postPrenom"
|
||||
type="text"
|
||||
value="Amélie"
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postNom"
|
||||
>
|
||||
Nom:
|
||||
<input
|
||||
id="postNom"
|
||||
name="postNom"
|
||||
type="text"
|
||||
value="Aupeix"
|
||||
/>
|
||||
</label>
|
||||
<label
|
||||
for="postMajeur"
|
||||
>
|
||||
Majeur:
|
||||
<input
|
||||
id="postMajeur"
|
||||
name="postMajeur"
|
||||
type="text"
|
||||
value="1"
|
||||
/>
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
>
|
||||
Save changes
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<MembrePage /> renders the loading status if data invalid 1`] = `
|
||||
<div
|
||||
class="membre"
|
||||
>
|
||||
<p>
|
||||
Oops! Failed to load data.
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<MembrePage /> renders the loading status if requesting data 1`] = `
|
||||
<div
|
||||
class="membre"
|
||||
>
|
||||
<p>
|
||||
Loading...
|
||||
</p>
|
||||
</div>
|
||||
`;
|
@ -1,18 +1,22 @@
|
||||
import { RouteComponentProps } from "react-router-dom"
|
||||
import React, { memo } from "react"
|
||||
import { useDispatch } from "react-redux"
|
||||
import { FC, memo } from "react"
|
||||
import { Helmet } from "react-helmet"
|
||||
import styles from "./styles.module.scss"
|
||||
import RegisterForm from "../../components/RegisterForm/RegisterForm"
|
||||
|
||||
export type Props = RouteComponentProps
|
||||
|
||||
const RegisterPage: React.FC<Props> = (): JSX.Element => (
|
||||
<div className={styles.registerPage}>
|
||||
<div className={styles.registerContent}>
|
||||
<Helmet title="RegisterPage" />
|
||||
<RegisterForm />
|
||||
const RegisterPage: FC<Props> = (): JSX.Element => {
|
||||
const dispatch = useDispatch()
|
||||
return (
|
||||
<div className={styles.registerPage}>
|
||||
<div className={styles.registerContent}>
|
||||
<Helmet title="RegisterPage" />
|
||||
<RegisterForm dispatch={dispatch} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(RegisterPage)
|
||||
|
@ -13,6 +13,7 @@ export default [
|
||||
routes: [
|
||||
{
|
||||
path: "/",
|
||||
exact: true,
|
||||
component: Register,
|
||||
},
|
||||
{
|
||||
@ -26,7 +27,6 @@ export default [
|
||||
},
|
||||
{
|
||||
path: "/register",
|
||||
exact: true,
|
||||
component: AsyncHome,
|
||||
loadData: loadHomeData,
|
||||
},
|
||||
|
@ -13,7 +13,12 @@ export default function getAccessors<
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
ElementNoId extends object,
|
||||
Element extends ElementNoId & ElementWithId
|
||||
>(sheetName: string, specimen: Element): any {
|
||||
>(sheetName: string, specimen: Element, translation: { [k in keyof Element]: string }): any {
|
||||
const frenchSpecimen = _.mapValues(
|
||||
_.invert(translation),
|
||||
(englishProp: string) => (specimen as any)[englishProp]
|
||||
) as Element
|
||||
|
||||
const addDBOperation = DBManager(sheetName)
|
||||
|
||||
async function listGet(): Promise<Element[]> {
|
||||
@ -27,10 +32,13 @@ export default function getAccessors<
|
||||
if (!rows[0]) {
|
||||
throw new Error(`No column types defined in sheet ${sheetName}`)
|
||||
}
|
||||
const types = _.pick(rows[0], Object.keys(specimen)) as Record<keyof Element, string>
|
||||
const types = _.pick(rows[0], Object.values(translation)) as Record<
|
||||
keyof Element,
|
||||
string
|
||||
>
|
||||
rows.shift()
|
||||
rows.forEach((row) => {
|
||||
const stringifiedElement = _.pick(row, Object.keys(specimen)) as Record<
|
||||
const stringifiedElement = _.pick(row, Object.values(translation)) as Record<
|
||||
keyof Element,
|
||||
string
|
||||
>
|
||||
@ -274,7 +282,7 @@ export default function getAccessors<
|
||||
}
|
||||
return element
|
||||
},
|
||||
JSON.parse(JSON.stringify(specimen))
|
||||
JSON.parse(JSON.stringify(frenchSpecimen))
|
||||
)
|
||||
return fullElement
|
||||
}
|
||||
@ -365,7 +373,7 @@ export default function getAccessors<
|
||||
|
||||
return stringifiedElement
|
||||
},
|
||||
JSON.parse(JSON.stringify(element))
|
||||
JSON.parse(JSON.stringify(frenchSpecimen))
|
||||
)
|
||||
|
||||
return rawElement
|
||||
|
@ -1,10 +1,10 @@
|
||||
import getExpressAccessors from "./expressAccessors"
|
||||
import { Envie, EnvieWithoutId } from "../../services/envies"
|
||||
import { Envie, EnvieWithoutId, translationEnvie } from "../../services/envies"
|
||||
|
||||
const { listGetRequest, getRequest, setRequest, addRequest } = getExpressAccessors<
|
||||
EnvieWithoutId,
|
||||
Envie
|
||||
>("Envies d'aider", new Envie())
|
||||
>("Envies d'aider", new Envie(), translationEnvie)
|
||||
|
||||
export const envieListGet = listGetRequest()
|
||||
|
||||
|
@ -5,8 +5,8 @@ export default function getExpressAccessors<
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
ElementNoId extends object,
|
||||
Element extends ElementNoId & ElementWithId
|
||||
>(sheetName: string, specimen: Element): any {
|
||||
const { get, listGet, add, set } = getAccessors(sheetName, specimen)
|
||||
>(sheetName: string, specimen: Element, translation: { [k in keyof Element]: string }): any {
|
||||
const { get, listGet, add, set } = getAccessors(sheetName, specimen, translation)
|
||||
|
||||
function listGetRequest() {
|
||||
return async (
|
||||
|
@ -1,10 +1,10 @@
|
||||
import getExpressAccessors from "./expressAccessors"
|
||||
import { JeuJav, JeuJavWithoutId } from "../../services/jeuxJav"
|
||||
import { JeuJav, JeuJavWithoutId, translationJeuJav } from "../../services/jeuxJav"
|
||||
|
||||
const { listGetRequest, getRequest, setRequest, addRequest } = getExpressAccessors<
|
||||
JeuJavWithoutId,
|
||||
JeuJav
|
||||
>("Jeux JAV", new JeuJav())
|
||||
>("Jeux JAV", new JeuJav(), translationJeuJav)
|
||||
|
||||
export const jeuJavListGet = listGetRequest()
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
import getExpressAccessors from "./expressAccessors"
|
||||
import { Membre, MembreWithoutId } from "../../services/membres"
|
||||
import { Membre, MembreWithoutId, translationMember } from "../../services/membres"
|
||||
|
||||
const { listGetRequest, getRequest, setRequest, addRequest } = getExpressAccessors<
|
||||
MembreWithoutId,
|
||||
Membre
|
||||
>("Membres", new Membre())
|
||||
>("Membres", new Membre(), translationMember)
|
||||
|
||||
export const membreListGet = listGetRequest()
|
||||
|
||||
|
15
src/server/gsheets/preMembers.ts
Normal file
15
src/server/gsheets/preMembers.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import getExpressAccessors from "./expressAccessors"
|
||||
import { PreMember, PreMemberWithoutId, translationPreMember } from "../../services/preMembers"
|
||||
|
||||
const { listGetRequest, getRequest, setRequest, addRequest } = getExpressAccessors<
|
||||
PreMemberWithoutId,
|
||||
PreMember
|
||||
>("PreMembres", new PreMember(), translationPreMember)
|
||||
|
||||
export const preMemberListGet = listGetRequest()
|
||||
|
||||
export const preMemberGet = getRequest()
|
||||
|
||||
export const preMemberAdd = addRequest()
|
||||
|
||||
export const preMemberSet = setRequest()
|
@ -18,6 +18,7 @@ import certbotRouter from "../routes/certbot"
|
||||
import { secure } from "./secure"
|
||||
import { jeuJavListGet } from "./gsheets/jeuJav"
|
||||
import { envieListGet, envieAdd } from "./gsheets/envies"
|
||||
import { preMemberAdd } from "./gsheets/preMembers"
|
||||
import { membreGet, membreSet } from "./gsheets/membres"
|
||||
import loginHandler from "./userManagement/login"
|
||||
import config from "../config"
|
||||
@ -56,6 +57,7 @@ app.post("/api/user/login", loginHandler)
|
||||
app.get("/JeuJavListGet", jeuJavListGet)
|
||||
app.get("/EnvieListGet", envieListGet)
|
||||
app.post("/EnvieAdd", envieAdd)
|
||||
app.post("/PreMemberAdd", preMemberAdd)
|
||||
|
||||
// Secured APIs
|
||||
app.get("/MembreGet", secure as RequestHandler, membreGet)
|
||||
|
@ -10,9 +10,9 @@ import { login } from "../login"
|
||||
// Full test with Bearer: wget --header='Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoicGlraW91c3ViQGdlYWlsLmNvbSIsInBlcm1pc3Npb25zIjpbXSwiaWF0IjoxNjM4MjUzODgzLCJleHAiOjE2Mzg4NTg2ODN9.MknJ4NfcVlgW2ODeimfwZI1a4z8asdEXtHwHgViy6c4' http://localhost:3000/MembreGet?id=1
|
||||
|
||||
const mockUser = {
|
||||
mail: "my.email@gmail.com",
|
||||
passe: "$2y$10$cuKFHEow2IVSZSPtoVsw6uZFNFOOP/v1V7fubbyvrxhZdsnxLHr.2",
|
||||
prenom: "monPrénom",
|
||||
email: "my.email@gmail.com",
|
||||
password: "$2y$10$cuKFHEow2IVSZSPtoVsw6uZFNFOOP/v1V7fubbyvrxhZdsnxLHr.2",
|
||||
firstname: "monPrénom",
|
||||
}
|
||||
|
||||
jest.mock("../../gsheets/accessors", () => () => ({
|
||||
@ -24,7 +24,7 @@ describe("login with", () => {
|
||||
const res = await login("my.email@gmail.com", "12345678")
|
||||
expect(_.omit(res, "jwt")).toEqual({
|
||||
membre: {
|
||||
prenom: mockUser.prenom,
|
||||
firstname: mockUser.firstname,
|
||||
},
|
||||
})
|
||||
expect(res.jwt).toBeDefined()
|
||||
|
@ -1,10 +1,16 @@
|
||||
import { Request, Response, NextFunction } from "express"
|
||||
import bcrypt from "bcrypt"
|
||||
import { Membre, MemberLogin, emailRegexp, passwordMinLength } from "../../services/membres"
|
||||
import {
|
||||
Membre,
|
||||
MemberLogin,
|
||||
emailRegexp,
|
||||
passwordMinLength,
|
||||
translationMember,
|
||||
} from "../../services/membres"
|
||||
import getAccessors from "../gsheets/accessors"
|
||||
import { getJwt } from "../secure"
|
||||
|
||||
const { listGet } = getAccessors("Membres", new Membre())
|
||||
const { listGet } = getAccessors("Membres", new Membre(), translationMember)
|
||||
|
||||
export default async function loginHandler(
|
||||
request: Request,
|
||||
@ -41,12 +47,12 @@ export async function login(rawEmail: string, rawPassword: string): Promise<Memb
|
||||
}
|
||||
|
||||
const membres: Membre[] = await listGet()
|
||||
const membre = membres.find((m) => m.mail === email)
|
||||
const membre = membres.find((m) => m.email === email)
|
||||
if (!membre) {
|
||||
throw Error("Cet email ne correspond à aucun utilisateur")
|
||||
}
|
||||
|
||||
const passwordMatch = await bcrypt.compare(password, membre.passe.replace(/^\$2y/, "$2a"))
|
||||
const passwordMatch = await bcrypt.compare(password, membre.password.replace(/^\$2y/, "$2a"))
|
||||
if (!passwordMatch) {
|
||||
throw Error("Mauvais mot de passe pour cet email")
|
||||
}
|
||||
@ -55,7 +61,7 @@ export async function login(rawEmail: string, rawPassword: string): Promise<Memb
|
||||
|
||||
return {
|
||||
membre: {
|
||||
prenom: membre.prenom,
|
||||
firstname: membre.firstname,
|
||||
},
|
||||
jwt,
|
||||
}
|
||||
|
@ -1,11 +1,17 @@
|
||||
import axios from "axios"
|
||||
import _ from "lodash"
|
||||
|
||||
import config from "../config"
|
||||
import { axiosConfig } from "./auth"
|
||||
|
||||
export type ElementWithId = unknown & { id: number }
|
||||
|
||||
export function get<Element>(elementName: string): (id: number) => Promise<{
|
||||
export type ElementTranslation = { [englishProp: string]: string }
|
||||
|
||||
export function get<Element>(
|
||||
elementName: string,
|
||||
translation: ElementTranslation
|
||||
): (id: number) => Promise<{
|
||||
data?: Element
|
||||
error?: Error
|
||||
}> {
|
||||
@ -19,14 +25,24 @@ export function get<Element>(elementName: string): (id: number) => Promise<{
|
||||
...axiosConfig,
|
||||
params: { id },
|
||||
})
|
||||
return { data }
|
||||
if (!data) {
|
||||
return { data }
|
||||
}
|
||||
const englishData = _.mapValues(
|
||||
translation,
|
||||
(frenchProp: string) => data[frenchProp]
|
||||
) as Element
|
||||
return { data: englishData }
|
||||
} catch (error) {
|
||||
return { error: error as Error }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function listGet<Element>(elementName: string): () => Promise<{
|
||||
export function listGet<Element>(
|
||||
elementName: string,
|
||||
translation: ElementTranslation
|
||||
): () => Promise<{
|
||||
data?: Element[]
|
||||
error?: Error
|
||||
}> {
|
||||
@ -37,7 +53,18 @@ export function listGet<Element>(elementName: string): () => Promise<{
|
||||
return async (): Promise<ElementListGetResponse> => {
|
||||
try {
|
||||
const { data } = await axios.get(`${config.API_URL}/${elementName}ListGet`, axiosConfig)
|
||||
return { data }
|
||||
if (!data) {
|
||||
return { data }
|
||||
}
|
||||
|
||||
const englishDataList = data.map(
|
||||
(frenchData: any) =>
|
||||
_.mapValues(
|
||||
translation,
|
||||
(frenchProp: string) => frenchData[frenchProp]
|
||||
) as Element
|
||||
)
|
||||
return { data: englishDataList }
|
||||
} catch (error) {
|
||||
return { error: error as Error }
|
||||
}
|
||||
@ -46,7 +73,8 @@ export function listGet<Element>(elementName: string): () => Promise<{
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export function add<ElementNoId extends object, Element extends ElementNoId & ElementWithId>(
|
||||
elementName: string
|
||||
elementName: string,
|
||||
translation: ElementTranslation
|
||||
): (membreWithoutId: ElementNoId) => Promise<{
|
||||
data?: Element
|
||||
error?: Error
|
||||
@ -57,19 +85,36 @@ export function add<ElementNoId extends object, Element extends ElementNoId & El
|
||||
}
|
||||
return async (membreWithoutId: ElementNoId): Promise<ElementGetResponse> => {
|
||||
try {
|
||||
const invertedTranslationWithoutId = _.invert(_.omit(translation, "id"))
|
||||
const frenchDataWithoutId = _.mapValues(
|
||||
invertedTranslationWithoutId,
|
||||
(englishProp: string, _frenchProp: string) => (membreWithoutId as any)[englishProp]
|
||||
)
|
||||
|
||||
const { data } = await axios.post(
|
||||
`${config.API_URL}/${elementName}Add`,
|
||||
membreWithoutId,
|
||||
frenchDataWithoutId,
|
||||
axiosConfig
|
||||
)
|
||||
return { data }
|
||||
if (!data) {
|
||||
return { data }
|
||||
}
|
||||
|
||||
const englishData = _.mapValues(
|
||||
translation,
|
||||
(frenchProp: string) => data[frenchProp]
|
||||
) as Element
|
||||
return { data: englishData }
|
||||
} catch (error) {
|
||||
return { error: error as Error }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function set<Element>(elementName: string): (membre: Element) => Promise<{
|
||||
export function set<Element>(
|
||||
elementName: string,
|
||||
translation: ElementTranslation
|
||||
): (membre: Element) => Promise<{
|
||||
data?: Element
|
||||
error?: Error
|
||||
}> {
|
||||
@ -79,12 +124,26 @@ export function set<Element>(elementName: string): (membre: Element) => Promise<
|
||||
}
|
||||
return async (membre: Element): Promise<ElementGetResponse> => {
|
||||
try {
|
||||
const invertedTranslation = _.invert(translation)
|
||||
const frenchData = _.mapValues(
|
||||
invertedTranslation,
|
||||
(englishProp: string) => (membre as any)[englishProp]
|
||||
)
|
||||
|
||||
const { data } = await axios.post(
|
||||
`${config.API_URL}/${elementName}Set`,
|
||||
membre,
|
||||
frenchData,
|
||||
axiosConfig
|
||||
)
|
||||
return { data }
|
||||
if (!data) {
|
||||
return { data }
|
||||
}
|
||||
|
||||
const englishData = _.mapValues(
|
||||
translation,
|
||||
(frenchProp: string) => data[frenchProp]
|
||||
) as Element
|
||||
return { data: englishData }
|
||||
} catch (error) {
|
||||
return { error: error as Error }
|
||||
}
|
||||
|
@ -3,23 +3,34 @@ import { get, listGet, add, set } from "./accessors"
|
||||
export class Envie {
|
||||
id = 0
|
||||
|
||||
domaine = ""
|
||||
domain = ""
|
||||
|
||||
envies = ""
|
||||
wish = ""
|
||||
|
||||
precisions = ""
|
||||
details = ""
|
||||
|
||||
equipes: string[] = []
|
||||
teams: string[] = []
|
||||
|
||||
dateAjout = ""
|
||||
addedDate = ""
|
||||
}
|
||||
|
||||
export const translationEnvie: { [k in keyof Envie]: string } = {
|
||||
id: "id",
|
||||
domain: "domaine",
|
||||
wish: "envies",
|
||||
details: "precisions",
|
||||
teams: "equipes",
|
||||
addedDate: "dateAjout",
|
||||
}
|
||||
|
||||
const elementName = "Envie"
|
||||
|
||||
export type EnvieWithoutId = Omit<Envie, "id">
|
||||
|
||||
export const envieGet = get<Envie>("Envie")
|
||||
export const envieGet = get<Envie>(elementName, translationEnvie)
|
||||
|
||||
export const envieListGet = listGet<Envie>("Envie")
|
||||
export const envieListGet = listGet<Envie>(elementName, translationEnvie)
|
||||
|
||||
export const envieAdd = add<EnvieWithoutId, Envie>("Envie")
|
||||
export const envieAdd = add<EnvieWithoutId, Envie>(elementName, translationEnvie)
|
||||
|
||||
export const envieSet = set<Envie>("Envie")
|
||||
export const envieSet = set<Envie>(elementName, translationEnvie)
|
||||
|
@ -3,17 +3,17 @@ import { get, listGet, add, set } from "./accessors"
|
||||
export class JeuJav {
|
||||
id = 0
|
||||
|
||||
titre = ""
|
||||
title = ""
|
||||
|
||||
auteur = ""
|
||||
author = ""
|
||||
|
||||
editeur = ""
|
||||
editor = ""
|
||||
|
||||
minJoueurs = 0
|
||||
playersMin = 0
|
||||
|
||||
maxJoueurs = 0
|
||||
playersMax = 0
|
||||
|
||||
duree = 0
|
||||
duration = 0
|
||||
|
||||
type: "Ambiance" | "Famille" | "Expert" | "" = ""
|
||||
|
||||
@ -21,23 +21,43 @@ export class JeuJav {
|
||||
|
||||
bggId = 0
|
||||
|
||||
exemplaires = 1
|
||||
copies = 1
|
||||
|
||||
dispoPret = 0
|
||||
lendAvailability = 0
|
||||
|
||||
nonRangee = 0
|
||||
notStored = 0
|
||||
|
||||
ean = ""
|
||||
|
||||
bggPhoto = ""
|
||||
}
|
||||
|
||||
export const translationJeuJav: { [k in keyof JeuJav]: string } = {
|
||||
id: "id",
|
||||
title: "titre",
|
||||
author: "auteur",
|
||||
editor: "editeur",
|
||||
playersMin: "minJoueurs",
|
||||
playersMax: "maxJoueurs",
|
||||
duration: "duree",
|
||||
type: "type",
|
||||
poufpaf: "poufpaf",
|
||||
bggId: "bggId",
|
||||
copies: "exemplaires",
|
||||
lendAvailability: "dispoPret",
|
||||
notStored: "nonRangee",
|
||||
ean: "ean",
|
||||
bggPhoto: "bggPhoto",
|
||||
}
|
||||
|
||||
const elementName = "JeuJav"
|
||||
|
||||
export type JeuJavWithoutId = Omit<JeuJav, "id">
|
||||
|
||||
export const jeuJavGet = get<JeuJav>("JeuJav")
|
||||
export const jeuJavGet = get<JeuJav>(elementName, translationJeuJav)
|
||||
|
||||
export const jeuJavListGet = listGet<JeuJav>("JeuJav")
|
||||
export const jeuJavListGet = listGet<JeuJav>(elementName, translationJeuJav)
|
||||
|
||||
export const jeuJavAdd = add<JeuJavWithoutId, JeuJav>("JeuJav")
|
||||
export const jeuJavAdd = add<JeuJavWithoutId, JeuJav>(elementName, translationJeuJav)
|
||||
|
||||
export const jeuJavSet = set<JeuJav>("JeuJav")
|
||||
export const jeuJavSet = set<JeuJav>(elementName, translationJeuJav)
|
||||
|
@ -3,38 +3,56 @@ import { get, listGet, add, set } from "./accessors"
|
||||
export class Membre {
|
||||
id = 0
|
||||
|
||||
nom = ""
|
||||
lastname = ""
|
||||
|
||||
prenom = ""
|
||||
firstname = ""
|
||||
|
||||
mail = ""
|
||||
email = ""
|
||||
|
||||
telephone = ""
|
||||
mobile = ""
|
||||
|
||||
photo = ""
|
||||
|
||||
alimentation = ""
|
||||
food = ""
|
||||
|
||||
majeur = 1
|
||||
adult = 1
|
||||
|
||||
privilege = 0
|
||||
privileges = 0
|
||||
|
||||
actif = 0
|
||||
active = 0
|
||||
|
||||
commentaire = ""
|
||||
comment = ""
|
||||
|
||||
horodatage = ""
|
||||
timestamp = ""
|
||||
|
||||
passe = ""
|
||||
password = ""
|
||||
}
|
||||
|
||||
export const translationMember: { [k in keyof Membre]: string } = {
|
||||
id: "id",
|
||||
lastname: "nom",
|
||||
firstname: "prenom",
|
||||
email: "mail",
|
||||
mobile: "telephone",
|
||||
photo: "photo",
|
||||
food: "alimentation",
|
||||
adult: "majeur",
|
||||
privileges: "privilege",
|
||||
active: "actif",
|
||||
comment: "commentaire",
|
||||
timestamp: "horodatage",
|
||||
password: "passe",
|
||||
}
|
||||
|
||||
const elementName = "Membre"
|
||||
|
||||
export const emailRegexp =
|
||||
/^(([^<>()[\].,;:\s@"]+(\.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i
|
||||
export const passwordMinLength = 4
|
||||
|
||||
export interface MemberLogin {
|
||||
membre?: {
|
||||
prenom: string
|
||||
firstname: string
|
||||
}
|
||||
jwt?: string
|
||||
error?: string
|
||||
@ -42,10 +60,10 @@ export interface MemberLogin {
|
||||
|
||||
export type MembreWithoutId = Omit<Membre, "id">
|
||||
|
||||
export const membreGet = get<Membre>("Membre")
|
||||
export const membreGet = get<Membre>(elementName, translationMember)
|
||||
|
||||
export const membreListGet = listGet<Membre>("Membre")
|
||||
export const membreListGet = listGet<Membre>(elementName, translationMember)
|
||||
|
||||
export const membreAdd = add<MembreWithoutId, Membre>("Membre")
|
||||
export const membreAdd = add<MembreWithoutId, Membre>(elementName, translationMember)
|
||||
|
||||
export const membreSet = set<Membre>("Membre")
|
||||
export const membreSet = set<Membre>(elementName, translationMember)
|
||||
|
39
src/services/preMembers.ts
Normal file
39
src/services/preMembers.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { get, listGet, add, set } from "./accessors"
|
||||
|
||||
export class PreMember {
|
||||
id = 0
|
||||
|
||||
firstname = ""
|
||||
|
||||
lastname = ""
|
||||
|
||||
email = ""
|
||||
|
||||
mobile = ""
|
||||
|
||||
alreadyVolunteer = false
|
||||
|
||||
comment = ""
|
||||
}
|
||||
|
||||
export const translationPreMember: { [k in keyof PreMember]: string } = {
|
||||
id: "id",
|
||||
firstname: "prenom",
|
||||
lastname: "nom",
|
||||
email: "email",
|
||||
mobile: "telephone",
|
||||
alreadyVolunteer: "dejaBenevole",
|
||||
comment: "commentaire",
|
||||
}
|
||||
|
||||
const elementName = "PreMember"
|
||||
|
||||
export type PreMemberWithoutId = Omit<PreMember, "id">
|
||||
|
||||
export const preMemberGet = get<PreMember>(elementName, translationPreMember)
|
||||
|
||||
export const preMemberListGet = listGet<PreMember>(elementName, translationPreMember)
|
||||
|
||||
export const preMemberAdd = add<PreMemberWithoutId, PreMember>(elementName, translationPreMember)
|
||||
|
||||
export const preMemberSet = set<PreMember>(elementName, translationPreMember)
|
@ -1,4 +1,5 @@
|
||||
import axios from "axios"
|
||||
import _ from "lodash"
|
||||
|
||||
import mockStore from "../../utils/mockStore"
|
||||
import JeuJavList, {
|
||||
@ -8,11 +9,12 @@ import JeuJavList, {
|
||||
getFailure,
|
||||
fetchJeuJavList,
|
||||
} from "../jeuJavList"
|
||||
import { JeuJav } from "../../services/jeuxJav"
|
||||
|
||||
jest.mock("axios")
|
||||
|
||||
const mockData = {
|
||||
"5": {
|
||||
const mockFrenchData: any[] = [
|
||||
{
|
||||
id: 5,
|
||||
titre: "6 qui prend!",
|
||||
auteur: "Wolfgang Kramer",
|
||||
@ -22,16 +24,35 @@ const mockData = {
|
||||
duree: 45,
|
||||
type: "Ambiance",
|
||||
poufpaf: "0-9-2/6-qui-prend-6-nimmt",
|
||||
photo: "https://cf.geekdo-images.com/thumb/img/lzczxR5cw7an7tRWeHdOrRtLyes=/fit-in/200x150/pic772547.jpg",
|
||||
bggPhoto: "",
|
||||
bggPhoto:
|
||||
"https://cf.geekdo-images.com/thumb/img/lzczxR5cw7an7tRWeHdOrRtLyes=/fit-in/200x150/pic772547.jpg",
|
||||
bggId: 432,
|
||||
exemplaires: 1,
|
||||
dispoPret: 1,
|
||||
nonRangee: 0,
|
||||
horodatage: "0000-00-00",
|
||||
ean: "3421272101313",
|
||||
},
|
||||
}
|
||||
]
|
||||
const mockEnglishData: JeuJav[] = [
|
||||
{
|
||||
id: 5,
|
||||
title: "6 qui prend!",
|
||||
author: "Wolfgang Kramer",
|
||||
editor: "(uncredited) , Design Edge , B",
|
||||
playersMin: 2,
|
||||
playersMax: 10,
|
||||
duration: 45,
|
||||
type: "Ambiance",
|
||||
poufpaf: "0-9-2/6-qui-prend-6-nimmt",
|
||||
bggPhoto:
|
||||
"https://cf.geekdo-images.com/thumb/img/lzczxR5cw7an7tRWeHdOrRtLyes=/fit-in/200x150/pic772547.jpg",
|
||||
bggId: 432,
|
||||
copies: 1,
|
||||
lendAvailability: 1,
|
||||
notStored: 0,
|
||||
ean: "3421272101313",
|
||||
},
|
||||
]
|
||||
const mockError = "Oops! Something went wrong."
|
||||
|
||||
describe("JeuJavList reducer", () => {
|
||||
@ -49,11 +70,11 @@ describe("JeuJavList reducer", () => {
|
||||
})
|
||||
|
||||
it("should handle success correctly", () => {
|
||||
expect(JeuJavList(undefined, { type: getSuccess.type, payload: mockData })).toEqual({
|
||||
expect(JeuJavList(undefined, { type: getSuccess.type, payload: mockEnglishData })).toEqual({
|
||||
...initialState,
|
||||
readyStatus: "success",
|
||||
ids: [5],
|
||||
entities: mockData,
|
||||
ids: _.map(mockEnglishData, "id"),
|
||||
entities: _.keyBy(mockEnglishData, "id"),
|
||||
})
|
||||
})
|
||||
|
||||
@ -70,12 +91,12 @@ describe("JeuJavList action", () => {
|
||||
it("fetches JeuJav list successful", async () => {
|
||||
const { dispatch, getActions } = mockStore()
|
||||
const expectedActions = [
|
||||
{ type: getRequesting.type },
|
||||
{ type: getSuccess.type, payload: mockData },
|
||||
{ type: getRequesting.type, payload: undefined },
|
||||
{ type: getSuccess.type, payload: mockEnglishData },
|
||||
]
|
||||
|
||||
// @ts-expect-error
|
||||
axios.get.mockResolvedValue({ data: mockData })
|
||||
axios.get.mockResolvedValue({ data: mockFrenchData })
|
||||
|
||||
await dispatch(fetchJeuJavList())
|
||||
expect(getActions()).toEqual(expectedActions)
|
||||
|
@ -2,10 +2,11 @@ import axios from "axios"
|
||||
|
||||
import mockStore from "../../utils/mockStore"
|
||||
import membre, { getRequesting, getSuccess, getFailure, fetchMembre, initialState } from "../membre"
|
||||
import { Membre } from "../../services/membres"
|
||||
|
||||
jest.mock("axios")
|
||||
|
||||
const mockData = {
|
||||
const mockFrenchData: any = {
|
||||
id: 1,
|
||||
nom: "Aupeix",
|
||||
prenom: "Amélie",
|
||||
@ -20,7 +21,23 @@ const mockData = {
|
||||
horodatage: "0000-00-00",
|
||||
passe: "$2y$10$fSxY9AIuxSiEjwF.J3eXGubIxUPlobkyRrNIal8ASimSjNj4SR.9O",
|
||||
}
|
||||
const { id } = mockData
|
||||
|
||||
const mockEnglishData: Membre = {
|
||||
id: 1,
|
||||
lastname: "Aupeix",
|
||||
firstname: "Amélie",
|
||||
email: "pakouille.lakouille@yahoo.fr",
|
||||
mobile: "0675650392",
|
||||
photo: "images/membres/$taille/amélie_aupeix.jpg",
|
||||
food: "Végétarien",
|
||||
adult: 1,
|
||||
privileges: 0,
|
||||
active: 0,
|
||||
comment: "",
|
||||
timestamp: "0000-00-00",
|
||||
password: "$2y$10$fSxY9AIuxSiEjwF.J3eXGubIxUPlobkyRrNIal8ASimSjNj4SR.9O",
|
||||
}
|
||||
const { id } = mockEnglishData
|
||||
const mockError = "Oops! Something went wrong."
|
||||
|
||||
describe("membre reducer", () => {
|
||||
@ -39,9 +56,9 @@ describe("membre reducer", () => {
|
||||
expect(
|
||||
membre(undefined, {
|
||||
type: getSuccess.type,
|
||||
payload: mockData,
|
||||
payload: mockEnglishData,
|
||||
})
|
||||
).toEqual({ readyStatus: "success", entity: mockData })
|
||||
).toEqual({ readyStatus: "success", entity: mockEnglishData })
|
||||
})
|
||||
|
||||
it("should handle failure correctly", () => {
|
||||
@ -58,12 +75,12 @@ describe("membre action", () => {
|
||||
it("fetches membre data successful", async () => {
|
||||
const { dispatch, getActions } = mockStore()
|
||||
const expectedActions = [
|
||||
{ type: getRequesting.type },
|
||||
{ type: getSuccess.type, payload: mockData },
|
||||
{ type: getRequesting.type, payload: undefined },
|
||||
{ type: getSuccess.type, payload: mockEnglishData },
|
||||
]
|
||||
|
||||
// @ts-expect-error
|
||||
axios.get.mockResolvedValue({ data: mockData })
|
||||
axios.get.mockResolvedValue({ data: mockFrenchData })
|
||||
|
||||
await dispatch(fetchMembre(id))
|
||||
expect(getActions()).toEqual(expectedActions)
|
||||
|
@ -1,4 +1,5 @@
|
||||
import axios from "axios"
|
||||
import _ from "lodash"
|
||||
|
||||
import mockStore from "../../utils/mockStore"
|
||||
import membreList, {
|
||||
@ -8,11 +9,12 @@ import membreList, {
|
||||
getFailure,
|
||||
fetchMembreList,
|
||||
} from "../membreList"
|
||||
import { Membre } from "../../services/membres"
|
||||
|
||||
jest.mock("axios")
|
||||
|
||||
const mockData = {
|
||||
"1": {
|
||||
const mockFrenchData: any[] = [
|
||||
{
|
||||
id: 1,
|
||||
nom: "Aupeix",
|
||||
prenom: "Amélie",
|
||||
@ -27,7 +29,25 @@ const mockData = {
|
||||
horodatage: "0000-00-00",
|
||||
passe: "$2y$10$fSxY9AIuxSiEjwF.J3eXGubIxUPlobkyRrNIal8ASimSjNj4SR.9O",
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
const mockEnglishData: Membre[] = [
|
||||
{
|
||||
id: 1,
|
||||
lastname: "Aupeix",
|
||||
firstname: "Amélie",
|
||||
email: "pakouille.lakouille@yahoo.fr",
|
||||
mobile: "0675650392",
|
||||
photo: "images/membres/$taille/amélie_aupeix.jpg",
|
||||
food: "Végétarien",
|
||||
adult: 1,
|
||||
privileges: 0,
|
||||
active: 0,
|
||||
comment: "",
|
||||
timestamp: "0000-00-00",
|
||||
password: "$2y$10$fSxY9AIuxSiEjwF.J3eXGubIxUPlobkyRrNIal8ASimSjNj4SR.9O",
|
||||
},
|
||||
]
|
||||
const mockError = "Oops! Something went wrong."
|
||||
|
||||
describe("membreList reducer", () => {
|
||||
@ -45,11 +65,11 @@ describe("membreList reducer", () => {
|
||||
})
|
||||
|
||||
it("should handle success correctly", () => {
|
||||
expect(membreList(undefined, { type: getSuccess.type, payload: mockData })).toEqual({
|
||||
expect(membreList(undefined, { type: getSuccess.type, payload: mockEnglishData })).toEqual({
|
||||
...initialState,
|
||||
readyStatus: "success",
|
||||
ids: [1],
|
||||
entities: mockData,
|
||||
ids: _.map(mockEnglishData, "id"),
|
||||
entities: _.keyBy(mockEnglishData, "id"),
|
||||
})
|
||||
})
|
||||
|
||||
@ -66,12 +86,12 @@ describe("membreList action", () => {
|
||||
it("fetches membre list successful", async () => {
|
||||
const { dispatch, getActions } = mockStore()
|
||||
const expectedActions = [
|
||||
{ type: getRequesting.type },
|
||||
{ type: getSuccess.type, payload: mockData },
|
||||
{ type: getRequesting.type, payload: undefined },
|
||||
{ type: getSuccess.type, payload: mockEnglishData },
|
||||
]
|
||||
|
||||
// @ts-expect-error
|
||||
axios.get.mockResolvedValue({ data: mockData })
|
||||
axios.get.mockResolvedValue({ data: mockFrenchData })
|
||||
|
||||
await dispatch(fetchMembreList())
|
||||
expect(getActions()).toEqual(expectedActions)
|
||||
|
38
src/store/preMemberAdd.ts
Normal file
38
src/store/preMemberAdd.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { PayloadAction, createSlice, createEntityAdapter } from "@reduxjs/toolkit"
|
||||
|
||||
import { StateRequest, elementAddFetch } from "./utils"
|
||||
import { PreMember, preMemberAdd } from "../services/preMembers"
|
||||
|
||||
const preMemberAdapter = createEntityAdapter<PreMember>()
|
||||
|
||||
const preMemberAddSlice = createSlice({
|
||||
name: "addPreMember",
|
||||
initialState: preMemberAdapter.getInitialState({
|
||||
readyStatus: "idle",
|
||||
} as StateRequest),
|
||||
reducers: {
|
||||
getRequesting: (state) => {
|
||||
state.readyStatus = "request"
|
||||
},
|
||||
getSuccess: (state, { payload }: PayloadAction<PreMember>) => {
|
||||
state.readyStatus = "success"
|
||||
preMemberAdapter.addOne(state, payload)
|
||||
},
|
||||
getFailure: (state, { payload }: PayloadAction<string>) => {
|
||||
state.readyStatus = "failure"
|
||||
state.error = payload
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export default preMemberAddSlice.reducer
|
||||
export const { getRequesting, getSuccess, getFailure } = preMemberAddSlice.actions
|
||||
|
||||
export const fetchPreMemberAdd = elementAddFetch(
|
||||
preMemberAdd,
|
||||
getRequesting,
|
||||
getSuccess,
|
||||
getFailure,
|
||||
() => null,
|
||||
() => null
|
||||
)
|
@ -8,6 +8,7 @@ import membre from "./membre"
|
||||
import membreAdd from "./membreAdd"
|
||||
import membreList from "./membreList"
|
||||
import membreSet from "./membreSet"
|
||||
import preMemberAdd from "./preMemberAdd"
|
||||
|
||||
// Use inferred return type for making correctly Redux types
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
@ -19,6 +20,7 @@ export default (history: History) => ({
|
||||
membreAdd,
|
||||
membreList,
|
||||
membreSet,
|
||||
preMemberAdd,
|
||||
router: connectRouter(history) as any,
|
||||
// Register more reducers...
|
||||
})
|
||||
|
@ -23,7 +23,7 @@ export function toastError(message: string): void {
|
||||
export function toastSuccess(message: string): void {
|
||||
toast.success(message, {
|
||||
position: "top-center",
|
||||
autoClose: 3000,
|
||||
autoClose: 5000,
|
||||
hideProgressBar: true,
|
||||
closeOnClick: true,
|
||||
pauseOnHover: true,
|
||||
|
@ -31,6 +31,7 @@ const config: Configuration = {
|
||||
}),
|
||||
new webpack.DefinePlugin({
|
||||
localStorage: { getItem: () => null, setItem: () => null, removeItem: () => null },
|
||||
"location.protocol": "http:",
|
||||
}),
|
||||
],
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user