mirror of
https://github.com/Paris-est-Ludique/intranet.git
synced 2025-06-08 08:34:20 +02:00
Update sign up message and available meetup location
This commit is contained in:
parent
2a1d2ef49b
commit
e6a049d0d1
@ -40,6 +40,7 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "yarn dev:build && nodemon ./public/server",
|
"dev": "yarn dev:build && nodemon ./public/server",
|
||||||
|
"ser": "yarn dev:build && node ./public/server",
|
||||||
"dev:build": "cross-env NODE_ENV=development webpack --config ./webpack/server.config.ts",
|
"dev:build": "cross-env NODE_ENV=development webpack --config ./webpack/server.config.ts",
|
||||||
"local-start": "cross-env LOCAL=true yarn build && node ./public/server",
|
"local-start": "cross-env LOCAL=true yarn build && node ./public/server",
|
||||||
"start": "node ./public/server",
|
"start": "node ./public/server",
|
||||||
|
@ -6,7 +6,7 @@ import { AskWelcome } from "./AskWelcome"
|
|||||||
import { AskBrunch, fetchFor as fetchForBrunch } from "./AskBrunch"
|
import { AskBrunch, fetchFor as fetchForBrunch } from "./AskBrunch"
|
||||||
import { AskRetex, fetchFor as fetchForRetex } from "./AskRetex"
|
import { AskRetex, fetchFor as fetchForRetex } from "./AskRetex"
|
||||||
import { AskDiscord, fetchFor as fetchForDiscord } from "./AskDiscord"
|
import { AskDiscord, fetchFor as fetchForDiscord } from "./AskDiscord"
|
||||||
// import { AskDayWishes, fetchFor as fetchForDayWishes } from "./AskDayWishes"
|
import { AskDayWishes, fetchFor as fetchForDayWishes } from "./AskDayWishes"
|
||||||
// import { AskHosting, fetchFor as fetchForHosting } from "./AskHosting"
|
// 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 { AskPersonalInfo, fetchFor as fetchForPersonalInfo } from "./AskPersonalInfo"
|
||||||
@ -26,7 +26,7 @@ const Asks = (): JSX.Element | null => {
|
|||||||
AskRetex(asks, 3)
|
AskRetex(asks, 3)
|
||||||
AskDiscord(asks, 5)
|
AskDiscord(asks, 5)
|
||||||
|
|
||||||
// AskDayWishes(asks, 10)
|
AskDayWishes(asks, 10)
|
||||||
// AskTeamWishes(asks, 11)
|
// AskTeamWishes(asks, 11)
|
||||||
// AskParticipationDetails(asks, 12)
|
// AskParticipationDetails(asks, 12)
|
||||||
// AskPersonalInfo(asks, 15)
|
// AskPersonalInfo(asks, 15)
|
||||||
@ -69,7 +69,7 @@ export const fetchFor = [
|
|||||||
...fetchForBrunch,
|
...fetchForBrunch,
|
||||||
...fetchForRetex,
|
...fetchForRetex,
|
||||||
...fetchForDiscord,
|
...fetchForDiscord,
|
||||||
// ...fetchForDayWishes,
|
...fetchForDayWishes,
|
||||||
// ...fetchForHosting,
|
// ...fetchForHosting,
|
||||||
// ...fetchForMeals,
|
// ...fetchForMeals,
|
||||||
// ...fetchForTeamWishes,
|
// ...fetchForTeamWishes,
|
||||||
|
@ -37,7 +37,7 @@ const KnowledgeBoxList: React.FC = (): JSX.Element | null => {
|
|||||||
onChange={onShowUnknownOnly}
|
onChange={onShowUnknownOnly}
|
||||||
checked={showUnknownOnly}
|
checked={showUnknownOnly}
|
||||||
/>{" "}
|
/>{" "}
|
||||||
Uniquement les non-renseignés
|
Afficher uniquement les non-renseignés
|
||||||
</label>
|
</label>
|
||||||
<ul className={styles.boxList}>
|
<ul className={styles.boxList}>
|
||||||
{boxesToShow.map((detailedBox: any) => (
|
{boxesToShow.map((detailedBox: any) => (
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
width: 280px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.boxList {
|
.boxList {
|
||||||
|
@ -1,156 +0,0 @@
|
|||||||
import { cloneDeep, pull, sortBy, uniq } from "lodash"
|
|
||||||
import React, { memo, useCallback, useEffect, useState } from "react"
|
|
||||||
import classnames from "classnames"
|
|
||||||
import styles from "./styles.module.scss"
|
|
||||||
import { DetailedBox } from "../../services/boxes"
|
|
||||||
import { VolunteerLoan, VolunteerLoanWithoutId } from "../../services/volunteers"
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
detailedBox: DetailedBox
|
|
||||||
volunteerLoan: VolunteerLoan | undefined
|
|
||||||
saveVolunteerLoan: (newVolunteerLoan: VolunteerLoan) => void
|
|
||||||
}
|
|
||||||
|
|
||||||
const BoxItem: React.FC<Props> = ({
|
|
||||||
detailedBox,
|
|
||||||
volunteerLoan,
|
|
||||||
saveVolunteerLoan,
|
|
||||||
}): JSX.Element => {
|
|
||||||
type ChoiceValue = keyof VolunteerLoanWithoutId
|
|
||||||
const [loanable, setLoanable] = useState(false)
|
|
||||||
const [playable, setPlayable] = useState(false)
|
|
||||||
const [giftable, setGiftable] = useState(false)
|
|
||||||
const [noOpinion, setNoOpinion] = useState(false)
|
|
||||||
const { gameId, bggPhoto, title, bggId, poufpaf, bggIdAlternative } = detailedBox
|
|
||||||
const isBggPhoto = !/^[0-9]+\.[a-zA-Z]+$/.test(bggPhoto)
|
|
||||||
const loanChoices: { name: string; value: ChoiceValue }[] = [
|
|
||||||
{ name: "Empruntable", value: "loanable" },
|
|
||||||
{ name: "Jouable", value: "playable" },
|
|
||||||
{ name: "Offrable", value: "giftable" },
|
|
||||||
{ name: "SansAvis", value: "noOpinion" },
|
|
||||||
]
|
|
||||||
const loanValues = {
|
|
||||||
loanable,
|
|
||||||
playable,
|
|
||||||
giftable,
|
|
||||||
noOpinion,
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
const loanSetters = {
|
|
||||||
loanable: setLoanable,
|
|
||||||
playable: setPlayable,
|
|
||||||
giftable: setGiftable,
|
|
||||||
noOpinion: setNoOpinion,
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!volunteerLoan) return
|
|
||||||
setLoanable(volunteerLoan.loanable.includes(gameId))
|
|
||||||
setPlayable(volunteerLoan.playable.includes(gameId))
|
|
||||||
setGiftable(volunteerLoan.giftable.includes(gameId))
|
|
||||||
setNoOpinion(volunteerLoan.noOpinion.includes(gameId))
|
|
||||||
}, [gameId, volunteerLoan])
|
|
||||||
|
|
||||||
const onChoiceClick = useCallback(
|
|
||||||
(value: ChoiceValue) => {
|
|
||||||
if (!volunteerLoan) return
|
|
||||||
const adding = !volunteerLoan[value].includes(gameId)
|
|
||||||
const newVolunteerLoan: VolunteerLoan = cloneDeep(volunteerLoan)
|
|
||||||
if (adding) {
|
|
||||||
newVolunteerLoan[value] = sortBy(uniq([...newVolunteerLoan[value], gameId]))
|
|
||||||
} else {
|
|
||||||
pull(newVolunteerLoan[value], gameId)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (adding && value === "noOpinion") {
|
|
||||||
pull(newVolunteerLoan.loanable, gameId)
|
|
||||||
pull(newVolunteerLoan.playable, gameId)
|
|
||||||
pull(newVolunteerLoan.giftable, gameId)
|
|
||||||
setLoanable(volunteerLoan.loanable.includes(gameId))
|
|
||||||
setPlayable(volunteerLoan.playable.includes(gameId))
|
|
||||||
setGiftable(volunteerLoan.giftable.includes(gameId))
|
|
||||||
setNoOpinion(true)
|
|
||||||
} else {
|
|
||||||
pull(newVolunteerLoan.noOpinion, gameId)
|
|
||||||
setNoOpinion(false)
|
|
||||||
loanSetters[value](adding)
|
|
||||||
}
|
|
||||||
saveVolunteerLoan(newVolunteerLoan)
|
|
||||||
},
|
|
||||||
[volunteerLoan, gameId, saveVolunteerLoan, loanSetters]
|
|
||||||
)
|
|
||||||
|
|
||||||
const onCheckboxChange = useCallback(() => {
|
|
||||||
// Do nothing
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<li className={styles.boxItem}>
|
|
||||||
<div className={styles.photoContainer}>
|
|
||||||
{isBggPhoto && <img className={styles.photo} src={bggPhoto} alt="" />}
|
|
||||||
{!isBggPhoto && (
|
|
||||||
<div
|
|
||||||
className={classnames(
|
|
||||||
styles.alternateBox,
|
|
||||||
styles[`a${bggPhoto.replace(/\..*/, "")}`]
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{" "}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className={classnames(styles.titleContainer, poufpaf && styles.shorterTitle)}>
|
|
||||||
<a
|
|
||||||
href={
|
|
||||||
bggId > 0
|
|
||||||
? `https://boardgamegeek.com/boardgame/${bggId}`
|
|
||||||
: bggIdAlternative
|
|
||||||
}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className={styles.title}
|
|
||||||
>
|
|
||||||
{title}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<a
|
|
||||||
href={`https://sites.google.com/site/poufpafpasteque/fiches_alpha/${poufpaf}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
<div className={poufpaf ? styles.poufpaf : styles.noPoufpaf}> </div>
|
|
||||||
</a>
|
|
||||||
<ul className={styles.loanList}>
|
|
||||||
{loanChoices.map(({ name, value }) => (
|
|
||||||
<li key={value} className={styles.loanItem}>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => onChoiceClick(value)}
|
|
||||||
className={classnames(
|
|
||||||
styles.loanButton,
|
|
||||||
styles[value],
|
|
||||||
loanValues[value] && styles.active
|
|
||||||
)}
|
|
||||||
data-message={name}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
className={styles.loanCheckbox}
|
|
||||||
checked={loanValues[value]}
|
|
||||||
onChange={() => onCheckboxChange()}
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
className={classnames(
|
|
||||||
styles.loanCheckboxImg,
|
|
||||||
styles[`${value}Checkbox`]
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default memo(BoxItem)
|
|
164
src/components/Loan/DetailedGameItem.tsx
Normal file
164
src/components/Loan/DetailedGameItem.tsx
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
// import { cloneDeep, pull, sortBy, uniq } from "lodash"
|
||||||
|
import { toast } from "react-toastify"
|
||||||
|
import React, { memo, useCallback, useEffect, useState } from "react"
|
||||||
|
import classnames from "classnames"
|
||||||
|
import styles from "./styles.module.scss"
|
||||||
|
import { DetailedBox } from "../../services/boxes"
|
||||||
|
import { VolunteerLoan, VolunteerLoanWithoutId } from "../../services/volunteers"
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
detailedGame: DetailedBox
|
||||||
|
volunteerLoan: VolunteerLoan | undefined
|
||||||
|
saveVolunteerLoan: (newVolunteerLoan: VolunteerLoan) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const DetailedGameItem: React.FC<Props> = ({
|
||||||
|
detailedGame,
|
||||||
|
volunteerLoan,
|
||||||
|
saveVolunteerLoan,
|
||||||
|
}): JSX.Element => {
|
||||||
|
type ChoiceValue = keyof VolunteerLoanWithoutId
|
||||||
|
const [loanable, setLoanable] = useState(false)
|
||||||
|
const [playable, setPlayable] = useState(false)
|
||||||
|
const [giftable, setGiftable] = useState(false)
|
||||||
|
const [noOpinion, setNoOpinion] = useState(false)
|
||||||
|
const { gameId, bggPhoto, title, bggId, poufpaf, bggIdAlternative } = detailedGame
|
||||||
|
const isBggPhoto = !/^[0-9]+\.[a-zA-Z]+$/.test(bggPhoto)
|
||||||
|
const loanChoices: { name: string; value: ChoiceValue }[] = [
|
||||||
|
{ name: "Empruntable", value: "loanable" },
|
||||||
|
{ name: "Jouable", value: "playable" },
|
||||||
|
{ name: "Offrable", value: "giftable" },
|
||||||
|
{ name: "SansAvis", value: "noOpinion" },
|
||||||
|
]
|
||||||
|
const loanValues = {
|
||||||
|
loanable,
|
||||||
|
playable,
|
||||||
|
giftable,
|
||||||
|
noOpinion,
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!volunteerLoan) return
|
||||||
|
setLoanable(volunteerLoan.loanable.includes(gameId))
|
||||||
|
setPlayable(volunteerLoan.playable.includes(gameId))
|
||||||
|
setGiftable(volunteerLoan.giftable.includes(gameId))
|
||||||
|
setNoOpinion(volunteerLoan.noOpinion.includes(gameId))
|
||||||
|
}, [gameId, volunteerLoan])
|
||||||
|
|
||||||
|
const onChoiceClick = useCallback(
|
||||||
|
(_value: ChoiceValue) => {
|
||||||
|
toast.warning("Il n'est plus possible de changer ses choix", {
|
||||||
|
position: "top-center",
|
||||||
|
autoClose: 500,
|
||||||
|
hideProgressBar: true,
|
||||||
|
closeOnClick: true,
|
||||||
|
pauseOnHover: true,
|
||||||
|
draggable: true,
|
||||||
|
progress: undefined,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
|
||||||
|
// (value: ChoiceValue) => {
|
||||||
|
// if (!volunteerLoan) return
|
||||||
|
// const adding = !volunteerLoan[value].includes(gameId)
|
||||||
|
// const newVolunteerLoan: VolunteerLoan = cloneDeep(volunteerLoan)
|
||||||
|
// if (adding) {
|
||||||
|
// newVolunteerLoan[value] = sortBy(uniq([...newVolunteerLoan[value], gameId]))
|
||||||
|
// } else {
|
||||||
|
// pull(newVolunteerLoan[value], gameId)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (adding && value === "noOpinion") {
|
||||||
|
// pull(newVolunteerLoan.loanable, gameId)
|
||||||
|
// pull(newVolunteerLoan.playable, gameId)
|
||||||
|
// pull(newVolunteerLoan.giftable, gameId)
|
||||||
|
// } else {
|
||||||
|
// pull(newVolunteerLoan.noOpinion, gameId)
|
||||||
|
// }
|
||||||
|
// saveVolunteerLoan(newVolunteerLoan)
|
||||||
|
// },
|
||||||
|
// [volunteerLoan, gameId, saveVolunteerLoan]
|
||||||
|
)
|
||||||
|
|
||||||
|
// Just to keep saveVolunteerLoan necessary to prevent errors while changes are prevented
|
||||||
|
if (!saveVolunteerLoan) {
|
||||||
|
console.log("")
|
||||||
|
}
|
||||||
|
|
||||||
|
const onCheckboxChange = useCallback(() => {
|
||||||
|
// Do nothing
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<li className={classnames(styles.boxItem, !volunteerLoan && styles.hide)}>
|
||||||
|
<div className={styles.photoContainer}>
|
||||||
|
{isBggPhoto && <img className={styles.photo} src={bggPhoto} alt="" />}
|
||||||
|
{!isBggPhoto && (
|
||||||
|
<div
|
||||||
|
className={classnames(
|
||||||
|
styles.alternateBox,
|
||||||
|
styles[`a${bggPhoto.replace(/\..*/, "")}`]
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{" "}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className={classnames(styles.titleContainer, poufpaf && styles.shorterTitle)}>
|
||||||
|
<a
|
||||||
|
href={
|
||||||
|
bggId > 0
|
||||||
|
? `https://boardgamegeek.com/boardgame/${bggId}`
|
||||||
|
: bggIdAlternative
|
||||||
|
}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className={styles.title}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
href={`https://sites.google.com/site/poufpafpasteque/fiches_alpha/${poufpaf}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
<div className={poufpaf ? styles.poufpaf : styles.noPoufpaf}> </div>
|
||||||
|
</a>
|
||||||
|
<ul className={styles.loanList}>
|
||||||
|
{loanChoices.map(({ name, value }) => (
|
||||||
|
<li key={value} className={styles.loanItem}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => onChoiceClick(value)}
|
||||||
|
className={classnames(
|
||||||
|
styles.loanButton,
|
||||||
|
styles[value],
|
||||||
|
loanValues[value] && styles.active
|
||||||
|
)}
|
||||||
|
data-message={name}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className={styles.loanCheckbox}
|
||||||
|
checked={loanValues[value]}
|
||||||
|
onChange={() => onCheckboxChange()}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className={classnames(
|
||||||
|
styles.loanCheckboxImg,
|
||||||
|
styles[`${value}Checkbox`]
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(DetailedGameItem)
|
64
src/components/Loan/GamesWithVolunteersItem.tsx
Normal file
64
src/components/Loan/GamesWithVolunteersItem.tsx
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import React, { memo } from "react"
|
||||||
|
import classnames from "classnames"
|
||||||
|
import styles from "./styles.module.scss"
|
||||||
|
import { GameWithVolunteers } from "../../services/games"
|
||||||
|
import { gameTitleExactCategory } from "../../store/utils"
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
gameWithLoans: GameWithVolunteers
|
||||||
|
}
|
||||||
|
|
||||||
|
const LoanGamesItem: React.FC<Props> = ({ gameWithLoans }): JSX.Element => {
|
||||||
|
const { bggPhoto, title, bggId, poufpaf, bggIdAlternative, volunteerNicknames, boxCount } =
|
||||||
|
gameWithLoans
|
||||||
|
const isBggPhoto = !/^[0-9]+\.[a-zA-Z]+$/.test(bggPhoto)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li className={styles.boxItem}>
|
||||||
|
<div className={styles.photoContainer}>
|
||||||
|
{isBggPhoto && <img className={styles.photo} src={bggPhoto} alt="" />}
|
||||||
|
{!isBggPhoto && (
|
||||||
|
<div
|
||||||
|
className={classnames(
|
||||||
|
styles.alternateBox,
|
||||||
|
styles[`a${bggPhoto.replace(/\..*/, "")}`]
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{" "}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className={classnames(styles.titleContainer, poufpaf && styles.shorterTitle)}>
|
||||||
|
<a
|
||||||
|
href={
|
||||||
|
bggId > 0
|
||||||
|
? `https://boardgamegeek.com/boardgame/${bggId}`
|
||||||
|
: bggIdAlternative
|
||||||
|
}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className={styles.title}
|
||||||
|
>
|
||||||
|
{title}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
href={`https://sites.google.com/site/poufpafpasteque/fiches_alpha/${poufpaf}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
<div className={poufpaf ? styles.poufpaf : styles.noPoufpaf}> </div>
|
||||||
|
</a>
|
||||||
|
<div className={styles.boxCount}>
|
||||||
|
{boxCount > 1 ? `${boxCount} boîtes` : ""}
|
||||||
|
<br />
|
||||||
|
{gameTitleExactCategory({ title })}
|
||||||
|
</div>
|
||||||
|
<div className={styles.volunteerList}>
|
||||||
|
Bénévoles intéressés: {volunteerNicknames.join(", ")}
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(LoanGamesItem)
|
@ -1,23 +1,21 @@
|
|||||||
import React, { memo, useState } from "react"
|
import React, { memo, useState } from "react"
|
||||||
import { useSelector } from "react-redux"
|
import { useSelector } from "react-redux"
|
||||||
import styles from "./styles.module.scss"
|
import styles from "./styles.module.scss"
|
||||||
import BoxItem from "./BoxItem"
|
|
||||||
import { fetchBoxListIfNeed, selectSortedUniqueDetailedBoxes } from "../../store/boxList"
|
import { fetchBoxListIfNeed, selectSortedUniqueDetailedBoxes } from "../../store/boxList"
|
||||||
import { fetchVolunteerLoanSetIfNeed, useVolunteerLoan } from "../../store/volunteerLoanSet"
|
import { fetchVolunteerLoanSetIfNeed, useVolunteerLoan } from "../../store/volunteerLoanSet"
|
||||||
|
import { DetailedBox } from "../../services/boxes"
|
||||||
|
import DetailedGameItem from "./DetailedGameItem"
|
||||||
|
|
||||||
const LoanBoxList: React.FC = (): JSX.Element | null => {
|
const Loaning: React.FC = (): JSX.Element | null => {
|
||||||
const detailedBoxes = useSelector(selectSortedUniqueDetailedBoxes)
|
const detailedBoxes: DetailedBox[] = useSelector(selectSortedUniqueDetailedBoxes)
|
||||||
const [volunteerLoan, saveVolunteerLoan] = useVolunteerLoan()
|
const [volunteerLoan, saveVolunteerLoan] = useVolunteerLoan()
|
||||||
const [showUnknownOnly, setShowUnknownOnly] = useState(false)
|
const [showUnknownOnly, setShowUnknownOnly] = useState(false)
|
||||||
|
|
||||||
const onShowUnknownOnly = (e: React.ChangeEvent<HTMLInputElement>) =>
|
const onShowUnknownOnly = (e: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
setShowUnknownOnly(e.target.checked)
|
setShowUnknownOnly(e.target.checked)
|
||||||
|
|
||||||
if (!detailedBoxes || detailedBoxes.length === 0) return null
|
if (!detailedBoxes || detailedBoxes.length === 0) return null
|
||||||
|
|
||||||
const boxesToShow = detailedBoxes.filter(
|
const boxesToShow = detailedBoxes.filter(
|
||||||
(box) =>
|
(box) =>
|
||||||
!box ||
|
|
||||||
!showUnknownOnly ||
|
!showUnknownOnly ||
|
||||||
!volunteerLoan ||
|
!volunteerLoan ||
|
||||||
(!volunteerLoan.loanable.includes(box.gameId) &&
|
(!volunteerLoan.loanable.includes(box.gameId) &&
|
||||||
@ -28,22 +26,22 @@ const LoanBoxList: React.FC = (): JSX.Element | null => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.loanThings}>
|
<div className={styles.loanThings}>
|
||||||
<label className={styles.showUnknownOnlyLabel}>
|
<label className={styles.showCheckboxLabel}>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="showUnknownOnly"
|
name="showUnknownOnly"
|
||||||
onChange={onShowUnknownOnly}
|
onChange={onShowUnknownOnly}
|
||||||
checked={showUnknownOnly}
|
checked={showUnknownOnly}
|
||||||
/>{" "}
|
/>{" "}
|
||||||
Uniquement les non-renseignés
|
Afficher uniquement les non-renseignés
|
||||||
</label>
|
</label>
|
||||||
<ul className={styles.boxList}>
|
<ul className={styles.boxList}>
|
||||||
{boxesToShow.map((detailedBox: any) => (
|
{boxesToShow.map((game) => (
|
||||||
<BoxItem
|
<DetailedGameItem
|
||||||
detailedBox={detailedBox}
|
detailedGame={game}
|
||||||
volunteerLoan={volunteerLoan}
|
volunteerLoan={volunteerLoan}
|
||||||
saveVolunteerLoan={saveVolunteerLoan}
|
saveVolunteerLoan={saveVolunteerLoan}
|
||||||
key={detailedBox.id}
|
key={game.id}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
@ -51,6 +49,6 @@ const LoanBoxList: React.FC = (): JSX.Element | null => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default memo(LoanBoxList)
|
export default memo(Loaning)
|
||||||
|
|
||||||
export const fetchFor = [fetchBoxListIfNeed, fetchVolunteerLoanSetIfNeed]
|
export const fetchFor = [fetchBoxListIfNeed, fetchVolunteerLoanSetIfNeed]
|
@ -2,7 +2,7 @@ import classnames from "classnames"
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import styles from "./styles.module.scss"
|
import styles from "./styles.module.scss"
|
||||||
|
|
||||||
const LoanIntro: React.FC = (): JSX.Element => (
|
const LoaningIntro: React.FC = (): JSX.Element => (
|
||||||
<div className={styles.loanThings}>
|
<div className={styles.loanThings}>
|
||||||
<h1>Emprunt et tri des jeux</h1>
|
<h1>Emprunt et tri des jeux</h1>
|
||||||
<p>
|
<p>
|
||||||
@ -54,4 +54,4 @@ const LoanIntro: React.FC = (): JSX.Element => (
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
export default LoanIntro
|
export default LoaningIntro
|
46
src/components/Loan/Loans.tsx
Normal file
46
src/components/Loan/Loans.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import React, { memo } from "react"
|
||||||
|
import { useSelector } from "react-redux"
|
||||||
|
import styles from "./styles.module.scss"
|
||||||
|
import {
|
||||||
|
fetchGameWithVolunteersListIfNeed,
|
||||||
|
selectSortedUniqueGamesWithVolunteers,
|
||||||
|
} from "../../store/gameWithVolunteersList"
|
||||||
|
import { GameWithVolunteers } from "../../services/games"
|
||||||
|
import GamesWithVolunteersItem from "./GamesWithVolunteersItem"
|
||||||
|
|
||||||
|
const Loans: React.FC = (): JSX.Element | null => {
|
||||||
|
const gameWithLoans: GameWithVolunteers[] = useSelector(selectSortedUniqueGamesWithVolunteers)
|
||||||
|
|
||||||
|
const loanGames = gameWithLoans.filter((game) => game.toLoan)
|
||||||
|
// const giftGames = gameWithLoans.filter(game => !game.toLoan)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h3>Jeux que tu veux emprunter</h3>
|
||||||
|
{loanGames.length > 0 && (
|
||||||
|
<ul className={styles.boxList}>
|
||||||
|
{loanGames.map((game) => (
|
||||||
|
<GamesWithVolunteersItem gameWithLoans={game} key={game.id} />
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
{loanGames.length === 0 && <>Aucun jeu.</>}
|
||||||
|
|
||||||
|
{/* {giftGames.length > 0 && <>
|
||||||
|
<h3>Jeux que tu voudrais récupérer définitivement</h3>
|
||||||
|
<ul className={styles.boxList}>
|
||||||
|
{giftGames.map(game => (
|
||||||
|
<GamesWithVolunteersItem
|
||||||
|
gameWithLoans={game}
|
||||||
|
key={game.id}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</>} */}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(Loans)
|
||||||
|
|
||||||
|
export const fetchFor = [fetchGameWithVolunteersListIfNeed]
|
14
src/components/Loan/LoansIntro.tsx
Normal file
14
src/components/Loan/LoansIntro.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import React from "react"
|
||||||
|
import styles from "./styles.module.scss"
|
||||||
|
|
||||||
|
const LoansIntro: React.FC = (): JSX.Element => (
|
||||||
|
<div className={styles.loanThings}>
|
||||||
|
<h1>Jeux à emprunter</h1>
|
||||||
|
<p>
|
||||||
|
Si un jeu qui t'intéresse est emprunté par un autre bénévole, son nom sera affiché ici.
|
||||||
|
Vous pourrez peut-être envisager d'y jouer ensemble !
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default LoansIntro
|
@ -1,11 +1,14 @@
|
|||||||
@import "../../theme/variables";
|
@import "../../theme/variables";
|
||||||
@import "../../theme/mixins";
|
@import "../../theme/mixins";
|
||||||
|
|
||||||
.showUnknownOnlyLabel {
|
.showCheckboxLabel {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
width: 280px;
|
}
|
||||||
|
|
||||||
|
.hide {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.boxList {
|
.boxList {
|
||||||
@ -200,6 +203,22 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.boxCount {
|
||||||
|
height: 50px;
|
||||||
|
width: 73px;
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.volunteerList {
|
||||||
|
height: 50px;
|
||||||
|
width: 198px;
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
.loanList {
|
.loanList {
|
||||||
@include clear-ul-style;
|
@include clear-ul-style;
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@ const MainMenu: FC = (): JSX.Element => {
|
|||||||
<MenuItem name="Questions" pathname="/" />
|
<MenuItem name="Questions" pathname="/" />
|
||||||
<MenuItem name="Annonces" pathname="/annonces" />
|
<MenuItem name="Annonces" pathname="/annonces" />
|
||||||
<MenuItem name="Mon profil" pathname="/profil" />
|
<MenuItem name="Mon profil" pathname="/profil" />
|
||||||
|
<MenuItem name="Emprunter" pathname="/emprunter" />
|
||||||
<MenuItem name="Emprunts" pathname="/emprunts" />
|
<MenuItem name="Emprunts" pathname="/emprunts" />
|
||||||
{/* <MenuItem name="Mes connaissances" pathname="/connaissances" /> */}
|
{/* <MenuItem name="Mes connaissances" pathname="/connaissances" /> */}
|
||||||
<RestrictMenuItem
|
<RestrictMenuItem
|
||||||
|
@ -8,7 +8,6 @@ import styles from "./styles.module.scss"
|
|||||||
|
|
||||||
import { fetchPostulantAdd } from "../../store/postulantAdd"
|
import { fetchPostulantAdd } from "../../store/postulantAdd"
|
||||||
import { AppDispatch, AppState } from "../../store"
|
import { AppDispatch, AppState } from "../../store"
|
||||||
import { fetchVolunteerPartialAdd } from "../../store/volunteerPartialAdd"
|
|
||||||
import FormButton from "../Form/FormButton/FormButton"
|
import FormButton from "../Form/FormButton/FormButton"
|
||||||
import { validEmail } from "../../utils/standardization"
|
import { validEmail } from "../../utils/standardization"
|
||||||
import { toastError } from "../../store/utils"
|
import { toastError } from "../../store/utils"
|
||||||
@ -38,24 +37,21 @@ const animations = [
|
|||||||
[styles.imgTransitionShow, styles.imgTransitionAbouToShow],
|
[styles.imgTransitionShow, styles.imgTransitionAbouToShow],
|
||||||
]
|
]
|
||||||
const RegisterForm = ({ dispatch }: Props): JSX.Element => {
|
const RegisterForm = ({ dispatch }: Props): JSX.Element => {
|
||||||
const [potentialVolunteer, setPotentialVolunteer] = useState(true)
|
|
||||||
const [firstname, setFirstname] = useState("")
|
const [firstname, setFirstname] = useState("")
|
||||||
const [lastname, setLastname] = useState("")
|
const [lastname, setLastname] = useState("")
|
||||||
const [email, setEmail] = useState("")
|
const [email, setEmail] = useState("")
|
||||||
const [mobile, setMobile] = useState("")
|
const [mobile, setMobile] = useState("")
|
||||||
const [alreadyVolunteer, setAlreadyVolunteer] = useState(false)
|
|
||||||
const [comment, setComment] = useState("")
|
const [comment, setComment] = useState("")
|
||||||
const [alreadyCame, setAlreadyCame] = useState(true)
|
const [alreadyCame, setAlreadyCame] = useState(true)
|
||||||
const [firstMeeting, setFirstMeeting] = useState("")
|
const [firstMeeting, setFirstMeeting] = useState("")
|
||||||
const [commentFirstMeeting, setCommentFirstMeeting] = useState("")
|
const [commentFirstMeeting, setCommentFirstMeeting] = useState("")
|
||||||
const [canHelpBefore, setCanHelpBefore] = useState("")
|
|
||||||
const [howToContact, setHowToContact] = useState("Email")
|
const [howToContact, setHowToContact] = useState("Email")
|
||||||
const [sending, setSending] = useState(false)
|
const [sending, setSending] = useState(false)
|
||||||
const [changingBackground, setChangingBackground] = useState(0)
|
const [changingBackground, setChangingBackground] = useState(0)
|
||||||
|
|
||||||
const meetingDateList = useSelector(selectMiscMeetingDateList)
|
const meetingDateList = useSelector(selectMiscMeetingDateList)
|
||||||
|
|
||||||
const enableRegistering = false
|
const enableRegistering = true
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timer = setInterval(() => {
|
const timer = setInterval(() => {
|
||||||
@ -83,47 +79,29 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (potentialVolunteer) {
|
dispatch(
|
||||||
dispatch(
|
fetchPostulantAdd({
|
||||||
fetchPostulantAdd({
|
potential: true,
|
||||||
potential: true,
|
firstname,
|
||||||
firstname,
|
lastname,
|
||||||
lastname,
|
email,
|
||||||
email,
|
mobile,
|
||||||
mobile,
|
howToContact,
|
||||||
howToContact,
|
alreadyCame,
|
||||||
alreadyCame,
|
firstMeeting,
|
||||||
firstMeeting,
|
commentFirstMeeting: firstMeeting ? "" : commentFirstMeeting,
|
||||||
commentFirstMeeting: firstMeeting ? "" : commentFirstMeeting,
|
comment,
|
||||||
comment,
|
})
|
||||||
})
|
)
|
||||||
)
|
// dispatch(
|
||||||
} else {
|
// fetchVolunteerPartialAdd({
|
||||||
dispatch(
|
// firstname,
|
||||||
fetchVolunteerPartialAdd({
|
// lastname,
|
||||||
firstname,
|
// email,
|
||||||
lastname,
|
// mobile,
|
||||||
email,
|
// howToContact,
|
||||||
mobile,
|
// })
|
||||||
howToContact,
|
// )
|
||||||
canHelpBefore,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
dispatch(
|
|
||||||
fetchPostulantAdd({
|
|
||||||
potential: false,
|
|
||||||
firstname,
|
|
||||||
lastname,
|
|
||||||
email,
|
|
||||||
mobile,
|
|
||||||
howToContact,
|
|
||||||
alreadyCame,
|
|
||||||
firstMeeting,
|
|
||||||
commentFirstMeeting: firstMeeting ? "" : commentFirstMeeting,
|
|
||||||
comment,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
setSending(true)
|
setSending(true)
|
||||||
}
|
}
|
||||||
@ -141,11 +119,7 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
|
|||||||
let sendSuccess
|
let sendSuccess
|
||||||
let sendError
|
let sendError
|
||||||
let sendingElement
|
let sendingElement
|
||||||
if (
|
if (!postulantError && !_.isEmpty(postulant)) {
|
||||||
!postulantError &&
|
|
||||||
!_.isEmpty(postulant) &&
|
|
||||||
(potentialVolunteer || (!volunteerError && !_.isEmpty(volunteer)))
|
|
||||||
) {
|
|
||||||
if (sending) {
|
if (sending) {
|
||||||
setSending(false)
|
setSending(false)
|
||||||
}
|
}
|
||||||
@ -181,11 +155,11 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
|
|||||||
<dd>
|
<dd>
|
||||||
<p>
|
<p>
|
||||||
Un festival en plein air dédié aux <b>jeux de société modernes</b> sous toutes
|
Un festival en plein air dédié aux <b>jeux de société modernes</b> sous toutes
|
||||||
leurs formes. Les samedi 2 et dimanche 3 juillet 2022 !
|
leurs formes. Les samedi 24 et dimanche 25 juin 2023 !
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
En 2019 lors de la dernière édition, ce sont <b>16 000</b> joueurs qui se sont
|
En 2022, ce sont <b>18 000</b> visiteurs qui sont venus sous 300 chapiteaux et 2
|
||||||
réunis sous 300 chapiteaux et 2 000 tables.
|
000 tables.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Les 2 jours que durent le festival sont entièrement dédiés à ce que le public{" "}
|
Les 2 jours que durent le festival sont entièrement dédiés à ce que le public{" "}
|
||||||
@ -211,7 +185,7 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
|
|||||||
toute l'année ^^
|
toute l'année ^^
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Pendant le festival de 2019, nous étions <b>187 bénévoles</b> organisés en
|
Pendant le festival de 2022, nous étions <b>196 bénévoles</b> organisés en
|
||||||
équipes qui chouchoutent les visiteurs en les accueillant, en s'assurant
|
é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.
|
que tout se passe bien, ou encore en expliquant des règles de jeux.
|
||||||
</p>
|
</p>
|
||||||
@ -224,9 +198,9 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
|
|||||||
<p>
|
<p>
|
||||||
La majorité d'entre nous sommes bénévoles les <b>samedi et dimanche</b>, mais
|
La majorité d'entre nous sommes bénévoles les <b>samedi et dimanche</b>, mais
|
||||||
certains bénévoles ne sont pas disponibles les deux jours. On leur demande alors
|
certains bénévoles ne sont pas disponibles les deux jours. On leur demande alors
|
||||||
d'aider à la mise en place jeudi ou vendredi, ou au rangement le lundi, à la
|
d'aider à la mise en place mercredi, jeudi ou vendredi, ou au rangement le
|
||||||
place d'un des jours du weekend. Bref, chacun participe comme il peut mais deux
|
lundi, à la place d'un des jours du weekend. Bref, chacun participe comme il
|
||||||
jours minimum !
|
peut mais <b>deux jours minimum</b> !
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Le samedi soir quand les visiteurs sont partis, nous prolongeons la fête en
|
Le samedi soir quand les visiteurs sont partis, nous prolongeons la fête en
|
||||||
@ -238,8 +212,8 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
|
|||||||
|
|
||||||
{!enableRegistering && (
|
{!enableRegistering && (
|
||||||
<dt>
|
<dt>
|
||||||
L'inscription est clôturée pour l'édition 2022, mais si l'expérience vous tente,
|
L'inscription est clôturée pour l'édition 2023, mais si l'expérience te tente,
|
||||||
remplissez le formulaire suivant pour devenir bénévole à PeL 2023 !<br />
|
remplis le formulaire suivant pour devenir bénévole à PeL 2024 !<br />
|
||||||
Dès septembre on se rencontrera sur Paris en petits groupes pour discuter du
|
Dès septembre on se rencontrera sur Paris en petits groupes pour discuter du
|
||||||
festival, du bénévolat et surtout faire connaissance :)
|
festival, du bénévolat et surtout faire connaissance :)
|
||||||
<br />
|
<br />
|
||||||
@ -248,98 +222,15 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
|
|||||||
|
|
||||||
{enableRegistering && (
|
{enableRegistering && (
|
||||||
<dt>
|
<dt>
|
||||||
Si l'expérience vous tente, remplissez le formulaire suivant pour devenir
|
Si tu as envie de donner le sourire à des milliers de gens, remplis le
|
||||||
bénévole !<br />
|
formulaire suivant pour rencontrer une poignée d'entre nous dans un bar/resto
|
||||||
Vous pouvez aussi juste nous rencontrer avant de vous décider à devenir
|
près de Châtelet, avant de te décider ou non à devenir bénévole :)
|
||||||
bénévole, on comprend qu'un saut pareil dans l'inconnu soit difficile.
|
|
||||||
<br />
|
|
||||||
Dans les deux cas, venez rencontrer une poignée d'entre nous dans un bar/resto
|
|
||||||
près de Châtelet ! :) Sur inscription uniquement...
|
|
||||||
<br />
|
<br />
|
||||||
</dt>
|
</dt>
|
||||||
)}
|
)}
|
||||||
</dl>
|
</dl>
|
||||||
)
|
)
|
||||||
|
|
||||||
const potentialVolunteerQuestion = enableRegistering && (
|
|
||||||
<div className={styles.inputWrapper}>
|
|
||||||
<div className={styles.leftCol}>
|
|
||||||
<div className={styles.multipleChoiceTitle}>Je veux devenir bénévole :</div>
|
|
||||||
</div>
|
|
||||||
<div className={styles.rightCol}>
|
|
||||||
{["Tout de suite !", "Peut-être après une rencontre avec des bénévoles"].map(
|
|
||||||
(option) => (
|
|
||||||
<label className={styles.longAnswerLabel} key={option}>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="potentialVolunteer"
|
|
||||||
onChange={sendBooleanRadioboxDispatch(
|
|
||||||
setPotentialVolunteer,
|
|
||||||
option !== "Tout de suite !"
|
|
||||||
)}
|
|
||||||
checked={potentialVolunteer === (option !== "Tout de suite !")}
|
|
||||||
/>{" "}
|
|
||||||
{option}
|
|
||||||
</label>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
|
|
||||||
const alreadyVolunteerQuestion = !potentialVolunteer && (
|
|
||||||
<>
|
|
||||||
<div className={styles.inputWrapper}>
|
|
||||||
<div className={styles.leftCol}>
|
|
||||||
<div className={styles.multipleChoiceTitle}>J'ai déjà été bénévole à PeL</div>
|
|
||||||
</div>
|
|
||||||
<div className={styles.rightCol}>
|
|
||||||
<div className={styles.rightColContainer}>
|
|
||||||
{["Oui", "Non"].map((option) => (
|
|
||||||
<label className={styles.shortAnswerLabel} key={option}>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="alreadyVolunteer"
|
|
||||||
onChange={sendBooleanRadioboxDispatch(
|
|
||||||
setAlreadyVolunteer,
|
|
||||||
option === "Oui"
|
|
||||||
)}
|
|
||||||
checked={alreadyVolunteer === (option === "Oui")}
|
|
||||||
/>{" "}
|
|
||||||
{option}
|
|
||||||
</label>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{alreadyVolunteer && (
|
|
||||||
<dl className={styles.registerIntro}>
|
|
||||||
<dd>
|
|
||||||
<p>Dans ce cas pourquoi t'inscris-tu ici ? ^^</p>
|
|
||||||
<p>
|
|
||||||
Si tu te rappelles de l'email que tu avais utilisé à ta dernière
|
|
||||||
inscription sur le site Force Orange des bénévoles (même sur l'ancienne
|
|
||||||
version) tu peux{" "}
|
|
||||||
<a href="/sidentifier" target="_blank" rel="noreferrer">
|
|
||||||
t'identifier ici
|
|
||||||
</a>{" "}
|
|
||||||
avec ton ancien mot de passe, ou en{" "}
|
|
||||||
<a href="/sinscrire" target="_blank" rel="noreferrer">
|
|
||||||
demander un nouveau ici
|
|
||||||
</a>
|
|
||||||
.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Autrement, si tu as changé d'email, mieux vaut nous le communiquer à
|
|
||||||
benevoles@parisestludique.fr en précisant bien tes nom et prénom :)
|
|
||||||
</p>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
|
|
||||||
const commentQuestion = (
|
const commentQuestion = (
|
||||||
<dl className={styles.inputWrapper}>
|
<dl className={styles.inputWrapper}>
|
||||||
<dd className={styles.commentWrapper}>
|
<dd className={styles.commentWrapper}>
|
||||||
@ -448,10 +339,7 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
|
|||||||
const meeting = enableRegistering && (
|
const meeting = enableRegistering && (
|
||||||
<>
|
<>
|
||||||
<dl className={styles.registerIntro}>
|
<dl className={styles.registerIntro}>
|
||||||
{!potentialVolunteer && <dt>Faisons connaissance !</dt>}
|
<dt>Faisons connaissance !</dt>
|
||||||
{potentialVolunteer && (
|
|
||||||
<dt>Se rencontrer avant de se décider à devenir bénévole ?</dt>
|
|
||||||
)}
|
|
||||||
<dd>
|
<dd>
|
||||||
<p>
|
<p>
|
||||||
On organise des rencontres entre de nouvelles personnes comme toi, et des
|
On organise des rencontres entre de nouvelles personnes comme toi, et des
|
||||||
@ -461,23 +349,13 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
|
|||||||
<p>
|
<p>
|
||||||
Ces rencontres ont lieu à 19h dans un bar/resto calme à Châtelet, le{" "}
|
Ces rencontres ont lieu à 19h dans un bar/resto calme à Châtelet, le{" "}
|
||||||
<a
|
<a
|
||||||
href="https://goo.gl/maps/N5NYWDF66vNQDFMh8"
|
href="https://g.page/lerhinocerosparis?share"
|
||||||
id="sfmMap"
|
id="sfmMap"
|
||||||
key="sfmMap"
|
key="sfmMap"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
>
|
>
|
||||||
Street Food Market
|
Rhinocéros
|
||||||
</a>
|
|
||||||
, ou à une soirée festive à 2 pas du lieu du festival, aux{" "}
|
|
||||||
<a
|
|
||||||
href="https://www.captainturtle.fr/aperos-petanque-paris/"
|
|
||||||
id="petanque"
|
|
||||||
key="petanque"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
apéros de la pétanque
|
|
||||||
</a>
|
</a>
|
||||||
.
|
.
|
||||||
</p>
|
</p>
|
||||||
@ -535,11 +413,7 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
|
|||||||
name="commentFirstMeeting"
|
name="commentFirstMeeting"
|
||||||
id="commentFirstMeeting"
|
id="commentFirstMeeting"
|
||||||
className={styles.inputWrapper}
|
className={styles.inputWrapper}
|
||||||
placeholder={
|
placeholder="Mince. Quelles dates t'arrangeraient ? Ou si c'est plus simple, quels jours sont à éviter ? Est-ce trop loin de chez toi ? Préfères-tu nous rencontrer en visio ?"
|
||||||
potentialVolunteer
|
|
||||||
? "Mince. Quelles dates t'arrangeraient ? Ou si c'est plus simple, quels jours sont à éviter ? Est-ce trop loin de chez toi ? Préfères-tu nous rencontrer en visio ?"
|
|
||||||
: "Ce n'est pas obligé mais ça aurait été top ! Manques-tu de temps ? Préfères-tu une autre date ? Est-ce trop loin de chez toi ? Préfères-tu nous rencontrer en visio ?"
|
|
||||||
}
|
|
||||||
value={commentFirstMeeting}
|
value={commentFirstMeeting}
|
||||||
onChange={sendTextareaDispatch(setCommentFirstMeeting)}
|
onChange={sendTextareaDispatch(setCommentFirstMeeting)}
|
||||||
/>
|
/>
|
||||||
@ -549,79 +423,6 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
|
|||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
|
||||||
const helpBefore = enableRegistering && !potentialVolunteer && (
|
|
||||||
<>
|
|
||||||
<dl className={styles.registerIntro}>
|
|
||||||
<dt>Bénévolat en amont du festival</dt>
|
|
||||||
<dd>
|
|
||||||
<p>
|
|
||||||
En tant que bénévole, tu fais selon tes envies, tes disponibilités, ton
|
|
||||||
énergie. Si personne ne veut faire quelque chose de primordial pour le
|
|
||||||
festival, on paye quelqu'un de l'extérieur. Par exemple le transport+montage
|
|
||||||
des tentes+tables, ou la sécurité de nuit sont délégués à des prestataires.
|
|
||||||
Et si ce quelque chose n'est pas primordiale et que personne ne veut s'en
|
|
||||||
occuper, bah tant pis on le fait pas ^^
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Après on essaye de faire plein de choses sans aide extérieure. Pour le
|
|
||||||
plaisir de collaborer à un projet entre bénévoles parfois devenus amis, pour
|
|
||||||
acquérir de nouvelles compétences, parce que chaque économie d'argent fait
|
|
||||||
baisser le prix d'entrée au festival et contribue à le rendre plus
|
|
||||||
accessible.
|
|
||||||
</p>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
<div className={styles.inputWrapper}>
|
|
||||||
<div className={styles.leftCol}>
|
|
||||||
<div className={styles.multipleChoiceTitle}>
|
|
||||||
Bref, as-tu le temps et l'envie de voir si tu peux aider en amont du
|
|
||||||
festival ?
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className={styles.rightCol}>
|
|
||||||
<div className={styles.rightColContainer}>
|
|
||||||
{[
|
|
||||||
{ value: "oui", desc: "Oui" },
|
|
||||||
{ value: "non", desc: "Non" },
|
|
||||||
{ value: "", desc: "Ne sais pas" },
|
|
||||||
].map((option) => (
|
|
||||||
<label className={styles.shortAnswerLabel} key={option.value}>
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="canHelpBefore"
|
|
||||||
value={option.value}
|
|
||||||
onChange={sendRadioboxDispatch(setCanHelpBefore)}
|
|
||||||
checked={canHelpBefore === option.value}
|
|
||||||
/>{" "}
|
|
||||||
{option.desc}
|
|
||||||
</label>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<dl className={styles.registerIntro}>
|
|
||||||
<dd>
|
|
||||||
{canHelpBefore === "oui" && (
|
|
||||||
<p>
|
|
||||||
Génial ! Quand tu auras fini de t'inscrire et que tu seras identifié sur
|
|
||||||
le site, nous t'en parlerons plus en détail.
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
{canHelpBefore === "non" && (
|
|
||||||
<p>
|
|
||||||
Aucun souci tu nous seras d'une aide précieuse le jour J c'est déjà
|
|
||||||
énorme !
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
<p>
|
|
||||||
Si tu changes d'avis, il sera possible de revenir sur cette décision dans
|
|
||||||
ton profil sur le site.
|
|
||||||
</p>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
|
|
||||||
const pelMember = enableRegistering && (
|
const pelMember = enableRegistering && (
|
||||||
<>
|
<>
|
||||||
<dl className={styles.registerIntro}>
|
<dl className={styles.registerIntro}>
|
||||||
@ -744,9 +545,7 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
|
|||||||
const submitButton = (
|
const submitButton = (
|
||||||
<>
|
<>
|
||||||
<div className={styles.buttonWrapper}>
|
<div className={styles.buttonWrapper}>
|
||||||
<FormButton onClick={onSubmit} disabled={!potentialVolunteer && alreadyVolunteer}>
|
<FormButton onClick={onSubmit}>Envoyer</FormButton>
|
||||||
Envoyer
|
|
||||||
</FormButton>
|
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.formReactions}>
|
<div className={styles.formReactions}>
|
||||||
{sendingElement}
|
{sendingElement}
|
||||||
@ -759,21 +558,13 @@ const RegisterForm = ({ dispatch }: Props): JSX.Element => {
|
|||||||
return (
|
return (
|
||||||
<form>
|
<form>
|
||||||
{intro}
|
{intro}
|
||||||
{potentialVolunteerQuestion}
|
{enableRegistering && commentQuestion}
|
||||||
{alreadyVolunteerQuestion}
|
{cameAsVisitor}
|
||||||
|
{meeting}
|
||||||
{(potentialVolunteer || !alreadyVolunteer) && (
|
{pelMember}
|
||||||
<>
|
{nameMobileEmail}
|
||||||
{enableRegistering && commentQuestion}
|
{!enableRegistering && commentQuestion}
|
||||||
{cameAsVisitor}
|
{howToContact !== "Aucun" && submitButton}
|
||||||
{meeting}
|
|
||||||
{helpBefore}
|
|
||||||
{pelMember}
|
|
||||||
{nameMobileEmail}
|
|
||||||
{!enableRegistering && commentQuestion}
|
|
||||||
{howToContact !== "Aucun" && submitButton}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</form>
|
</form>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { FC, memo } from "react"
|
import { FC, memo } from "react"
|
||||||
// import DayWishes from "./DayWishes/DayWishes"
|
import DayWishes from "./DayWishes/DayWishes"
|
||||||
// import DayWishesFormModal from "./DayWishesForm/DayWishesFormModal"
|
import DayWishesFormModal from "./DayWishesForm/DayWishesFormModal"
|
||||||
// import Hosting from "./Hosting/Hosting"
|
// import Hosting from "./Hosting/Hosting"
|
||||||
// import HostingFormModal from "./HostingForm/HostingFormModal"
|
// import HostingFormModal from "./HostingForm/HostingFormModal"
|
||||||
// import Meals from "./Meals/Meals"
|
// import Meals from "./Meals/Meals"
|
||||||
@ -12,7 +12,7 @@ import { FC, memo } from "react"
|
|||||||
// import VolunteerTeam from "./VolunteerTeam/VolunteerTeam"
|
// import VolunteerTeam from "./VolunteerTeam/VolunteerTeam"
|
||||||
import withUserConnected from "../../utils/withUserConnected"
|
import withUserConnected from "../../utils/withUserConnected"
|
||||||
import ContentTitle from "../ui/Content/ContentTitle"
|
import ContentTitle from "../ui/Content/ContentTitle"
|
||||||
// import { fetchFor as fetchForDayWishesForm } from "./DayWishesForm/DayWishesForm"
|
import { fetchFor as fetchForDayWishesForm } from "./DayWishesForm/DayWishesForm"
|
||||||
// import { fetchFor as fetchForHostingForm } from "./HostingForm/HostingForm"
|
// import { fetchFor as fetchForHostingForm } from "./HostingForm/HostingForm"
|
||||||
// import { fetchFor as fetchForMealsForm } from "./MealsForm/MealsForm"
|
// import { fetchFor as fetchForMealsForm } from "./MealsForm/MealsForm"
|
||||||
// import { fetchFor as fetchForParticipationDetailsForm } from "./ParticipationDetailsForm/ParticipationDetailsForm"
|
// import { fetchFor as fetchForParticipationDetailsForm } from "./ParticipationDetailsForm/ParticipationDetailsForm"
|
||||||
@ -39,9 +39,9 @@ const Board: FC = (): JSX.Element => {
|
|||||||
{retex && <BrunchFormModal />}
|
{retex && <BrunchFormModal />}
|
||||||
{retex && <Retex />}
|
{retex && <Retex />}
|
||||||
{retex && <RetexFormModal />}
|
{retex && <RetexFormModal />}
|
||||||
{/* <DayWishes />
|
<DayWishes />
|
||||||
<DayWishesFormModal />
|
<DayWishesFormModal />
|
||||||
<ParticipationDetails />
|
{/* <ParticipationDetails />
|
||||||
<ParticipationDetailsFormModal />
|
<ParticipationDetailsFormModal />
|
||||||
<TeamWishes />
|
<TeamWishes />
|
||||||
<TeamWishesFormModal />
|
<TeamWishesFormModal />
|
||||||
@ -60,7 +60,7 @@ export const fetchFor = [
|
|||||||
...fetchForPersonalInfoForm,
|
...fetchForPersonalInfoForm,
|
||||||
...fetchForRetexForm,
|
...fetchForRetexForm,
|
||||||
...fetchForBrunchForm,
|
...fetchForBrunchForm,
|
||||||
// ...fetchForDayWishesForm,
|
...fetchForDayWishesForm,
|
||||||
// ...fetchForHostingForm,
|
// ...fetchForHostingForm,
|
||||||
// ...fetchForMealsForm,
|
// ...fetchForMealsForm,
|
||||||
// ...fetchForParticipationDetailsForm,
|
// ...fetchForParticipationDetailsForm,
|
||||||
|
@ -27,7 +27,10 @@ const Brunch: FC<Props> = (): JSX.Element | null => {
|
|||||||
)}
|
)}
|
||||||
{question1 === 0 && <>Je ne viendrai pas au brunch </>}
|
{question1 === 0 && <>Je ne viendrai pas au brunch </>}
|
||||||
{question1 > 0 && (
|
{question1 > 0 && (
|
||||||
<>Je viendrai au brunch{question1 > 1 && <> avec {question1} personne(s)</>}</>
|
<>
|
||||||
|
Je viendrai au brunch
|
||||||
|
{question1 > 1 && <> avec {+question1 - 1} personne(s)</>}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.editButton}>
|
<div className={styles.editButton}>
|
||||||
|
@ -18,22 +18,22 @@ const DayWishes: FC = (): JSX.Element | null => {
|
|||||||
<div className={styles.title}>Mes présences</div>
|
<div className={styles.title}>Mes présences</div>
|
||||||
{participation === "non" && (
|
{participation === "non" && (
|
||||||
<div className={styles.participationLabel}>
|
<div className={styles.participationLabel}>
|
||||||
Je <b>ne participerai pas</b> à PeL 2022 :(
|
Je <b>ne participerai pas</b> à PeL 2023 :(
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{participation === "oui" && (
|
{participation === "oui" && (
|
||||||
<div className={styles.participationLabel}>
|
<div className={styles.participationLabel}>
|
||||||
Je <b className={styles.yesParticipation}>participerai</b> à PeL 2022 !
|
Je <b className={styles.yesParticipation}>participerai</b> à PeL 2023 !
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{participation === "peut-etre" && (
|
{participation === "peut-etre" && (
|
||||||
<div className={styles.participationLabel}>
|
<div className={styles.participationLabel}>
|
||||||
Je <b>ne sais pas encore</b> si je participerai à PeL 2022
|
Je <b>ne sais pas encore</b> si je participerai à PeL 2023
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{participation === "inconnu" && (
|
{participation === "inconnu" && (
|
||||||
<div className={styles.lineEmpty}>
|
<div className={styles.lineEmpty}>
|
||||||
Participation à PeL 2022{" "}
|
Participation à PeL 2023{" "}
|
||||||
<span className={styles.lineEmpty}>non renseignées</span>
|
<span className={styles.lineEmpty}>non renseignées</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -72,7 +72,7 @@ const DayWishesForm: FC<Props> = ({ children, afterSubmit }): JSX.Element => {
|
|||||||
<div className={styles.leftCol}>
|
<div className={styles.leftCol}>
|
||||||
<div className={styles.participationTitle}>
|
<div className={styles.participationTitle}>
|
||||||
Si les conditions sanitaires te le permettent, souhaites-tu être bénévole à
|
Si les conditions sanitaires te le permettent, souhaites-tu être bénévole à
|
||||||
PeL 2022 ?
|
PeL les 24 & 25 juin 2023 ?
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.rightCol}>
|
<div className={styles.rightCol}>
|
||||||
@ -124,6 +124,8 @@ const DayWishesForm: FC<Props> = ({ children, afterSubmit }): JSX.Element => {
|
|||||||
<div className={styles.leftCol}>
|
<div className={styles.leftCol}>
|
||||||
<div className={styles.dayWishesTitle}>
|
<div className={styles.dayWishesTitle}>
|
||||||
Quels jours viendras-tu ?<br />
|
Quels jours viendras-tu ?<br />
|
||||||
|
Il est tôt pour le dire, tu peux y repondre plus tard sur la page profil.
|
||||||
|
<br />
|
||||||
(Minimum 2 jours dont l'un sera samedi ou dimanche, idéalement samedi{" "}
|
(Minimum 2 jours dont l'un sera samedi ou dimanche, idéalement samedi{" "}
|
||||||
<b>et</b> dimanche ^^)
|
<b>et</b> dimanche ^^)
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,8 +8,10 @@ import DayWishesForm, {
|
|||||||
import ErrorBoundary from "./ErrorBoundary"
|
import ErrorBoundary from "./ErrorBoundary"
|
||||||
import GameList from "./GameList"
|
import GameList from "./GameList"
|
||||||
import Loading from "./Loading"
|
import Loading from "./Loading"
|
||||||
import LoanBoxList, { fetchFor as fetchForLoan } from "./Loan/LoanBoxList"
|
import LoaningIntro from "./Loan/LoaningIntro"
|
||||||
import LoanIntro from "./Loan/LoanIntro"
|
import Loaning, { fetchFor as fetchForLoaning } from "./Loan/Loaning"
|
||||||
|
import LoansIntro from "./Loan/LoansIntro"
|
||||||
|
import Loans, { fetchFor as fetchForLoans } from "./Loan/Loans"
|
||||||
import LoginForm from "./LoginForm"
|
import LoginForm from "./LoginForm"
|
||||||
import KnowledgeBoxList, { fetchFor as fetchForKnowledge } from "./Knowledge/KnowledgeBoxList"
|
import KnowledgeBoxList, { fetchFor as fetchForKnowledge } from "./Knowledge/KnowledgeBoxList"
|
||||||
import KnowledgeCard, { fetchFor as fetchForKnowledgeCard } from "./Knowledge/KnowledgeCard"
|
import KnowledgeCard, { fetchFor as fetchForKnowledgeCard } from "./Knowledge/KnowledgeCard"
|
||||||
@ -45,9 +47,12 @@ export {
|
|||||||
fetchForKnowledge,
|
fetchForKnowledge,
|
||||||
KnowledgeIntro,
|
KnowledgeIntro,
|
||||||
Loading,
|
Loading,
|
||||||
LoanBoxList,
|
LoaningIntro,
|
||||||
fetchForLoan,
|
Loaning,
|
||||||
LoanIntro,
|
fetchForLoaning,
|
||||||
|
LoansIntro,
|
||||||
|
Loans,
|
||||||
|
fetchForLoans,
|
||||||
LoginForm,
|
LoginForm,
|
||||||
Asks,
|
Asks,
|
||||||
fetchForAsks,
|
fetchForAsks,
|
||||||
|
33
src/pages/Loaning/LoaningPage.tsx
Normal file
33
src/pages/Loaning/LoaningPage.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { FC, memo } from "react"
|
||||||
|
import { useSelector } from "react-redux"
|
||||||
|
import { RouteComponentProps } from "react-router-dom"
|
||||||
|
import { Helmet } from "react-helmet"
|
||||||
|
|
||||||
|
import { AppThunk } from "../../store"
|
||||||
|
import styles from "./styles.module.scss"
|
||||||
|
import { LoaningIntro, Loaning, fetchForLoaning } from "../../components"
|
||||||
|
import { selectUserJwtToken } from "../../store/auth"
|
||||||
|
|
||||||
|
export type Props = RouteComponentProps
|
||||||
|
|
||||||
|
const LoaningPage: FC<Props> = (): JSX.Element => {
|
||||||
|
const jwtToken = useSelector(selectUserJwtToken)
|
||||||
|
if (jwtToken === undefined) return <p>Loading...</p>
|
||||||
|
if (!jwtToken) {
|
||||||
|
return <div>Besoin d'être identifié</div>
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className={styles.loaningPage}>
|
||||||
|
<div className={styles.loaningContent}>
|
||||||
|
<Helmet title="LoaningPage" />
|
||||||
|
<LoaningIntro />
|
||||||
|
<Loaning />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch server-side data here
|
||||||
|
export const loadData = (): AppThunk[] => [...fetchForLoaning.map((f) => f())]
|
||||||
|
|
||||||
|
export default memo(LoaningPage)
|
16
src/pages/Loaning/index.tsx
Executable file
16
src/pages/Loaning/index.tsx
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
import loadable from "@loadable/component"
|
||||||
|
|
||||||
|
import { Loading, ErrorBoundary } from "../../components"
|
||||||
|
import { Props, loadData } from "./LoaningPage"
|
||||||
|
|
||||||
|
const Loaning = loadable(() => import("./LoaningPage"), {
|
||||||
|
fallback: <Loading />,
|
||||||
|
})
|
||||||
|
|
||||||
|
export default (props: Props): JSX.Element => (
|
||||||
|
<ErrorBoundary>
|
||||||
|
<Loaning {...props} />
|
||||||
|
</ErrorBoundary>
|
||||||
|
)
|
||||||
|
|
||||||
|
export { loadData }
|
@ -1,9 +1,13 @@
|
|||||||
@import "../../theme/mixins";
|
@import "../../theme/mixins";
|
||||||
|
|
||||||
.loanPage {
|
.loaningPage {
|
||||||
@include page-wrapper-center;
|
@include page-wrapper-center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loanContent {
|
.loaningContent {
|
||||||
@include page-content-wrapper(700px);
|
@include page-content-wrapper(700px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hide {
|
||||||
|
display: none;
|
||||||
|
}
|
@ -5,29 +5,29 @@ import { Helmet } from "react-helmet"
|
|||||||
|
|
||||||
import { AppThunk } from "../../store"
|
import { AppThunk } from "../../store"
|
||||||
import styles from "./styles.module.scss"
|
import styles from "./styles.module.scss"
|
||||||
import { LoanBoxList, LoanIntro, fetchForLoan } from "../../components"
|
import { LoansIntro, Loans, fetchForLoans } from "../../components"
|
||||||
import { selectUserJwtToken } from "../../store/auth"
|
import { selectUserJwtToken } from "../../store/auth"
|
||||||
|
|
||||||
export type Props = RouteComponentProps
|
export type Props = RouteComponentProps
|
||||||
|
|
||||||
const LoanPage: FC<Props> = (): JSX.Element => {
|
const LoansPage: FC<Props> = (): JSX.Element => {
|
||||||
const jwtToken = useSelector(selectUserJwtToken)
|
const jwtToken = useSelector(selectUserJwtToken)
|
||||||
if (jwtToken === undefined) return <p>Loading...</p>
|
if (jwtToken === undefined) return <p>Loading...</p>
|
||||||
if (!jwtToken) {
|
if (!jwtToken) {
|
||||||
return <div>Besoin d'être identifié</div>
|
return <div>Besoin d'être identifié</div>
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className={styles.loanPage}>
|
<div className={styles.loaningPage}>
|
||||||
<div className={styles.loanContent}>
|
<div className={styles.loaningContent}>
|
||||||
<Helmet title="LoanPage" />
|
<Helmet title="LoansPage" />
|
||||||
<LoanIntro />
|
<LoansIntro />
|
||||||
<LoanBoxList />
|
<Loans />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch server-side data here
|
// Fetch server-side data here
|
||||||
export const loadData = (): AppThunk[] => [...fetchForLoan.map((f) => f())]
|
export const loadData = (): AppThunk[] => [...fetchForLoans.map((f) => f())]
|
||||||
|
|
||||||
export default memo(LoanPage)
|
export default memo(LoansPage)
|
@ -1,15 +1,15 @@
|
|||||||
import loadable from "@loadable/component"
|
import loadable from "@loadable/component"
|
||||||
|
|
||||||
import { Loading, ErrorBoundary } from "../../components"
|
import { Loading, ErrorBoundary } from "../../components"
|
||||||
import { Props, loadData } from "./LoanPage"
|
import { Props, loadData } from "./LoansPage"
|
||||||
|
|
||||||
const Loan = loadable(() => import("./LoanPage"), {
|
const Loans = loadable(() => import("./LoansPage"), {
|
||||||
fallback: <Loading />,
|
fallback: <Loading />,
|
||||||
})
|
})
|
||||||
|
|
||||||
export default (props: Props): JSX.Element => (
|
export default (props: Props): JSX.Element => (
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<Loan {...props} />
|
<Loans {...props} />
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
)
|
)
|
||||||
|
|
13
src/pages/Loans/styles.module.scss
Executable file
13
src/pages/Loans/styles.module.scss
Executable file
@ -0,0 +1,13 @@
|
|||||||
|
@import "../../theme/mixins";
|
||||||
|
|
||||||
|
.loaningPage {
|
||||||
|
@include page-wrapper-center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loaningContent {
|
||||||
|
@include page-content-wrapper(700px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hide {
|
||||||
|
display: none;
|
||||||
|
}
|
@ -10,7 +10,8 @@ import AsyncAnnouncements, { loadData as loadAnnouncementsData } from "../pages/
|
|||||||
import AsyncTeamAssignment, { loadData as loadTeamAssignmentData } from "../pages/TeamAssignment"
|
import AsyncTeamAssignment, { loadData as loadTeamAssignmentData } from "../pages/TeamAssignment"
|
||||||
import AsyncRegisterPage, { loadData as loadRegisterPage } from "../pages/Register"
|
import AsyncRegisterPage, { loadData as loadRegisterPage } from "../pages/Register"
|
||||||
import AsyncKnowledge, { loadData as loadKnowledgeData } from "../pages/Knowledge"
|
import AsyncKnowledge, { loadData as loadKnowledgeData } from "../pages/Knowledge"
|
||||||
import AsyncLoan, { loadData as loadLoanData } from "../pages/Loan"
|
import AsyncLoans, { loadData as loadLoansData } from "../pages/Loans"
|
||||||
|
import AsyncLoaning, { loadData as loadLoaningData } from "../pages/Loaning"
|
||||||
import AsyncKnowledgeCards, { loadData as loadCardKnowledgeData } from "../pages/KnowledgeCards"
|
import AsyncKnowledgeCards, { loadData as loadCardKnowledgeData } from "../pages/KnowledgeCards"
|
||||||
import AsyncTeams, { loadData as loadTeamsData } from "../pages/Teams"
|
import AsyncTeams, { loadData as loadTeamsData } from "../pages/Teams"
|
||||||
import AsyncBoard, { loadData as loadBoardData } from "../pages/Board"
|
import AsyncBoard, { loadData as loadBoardData } from "../pages/Board"
|
||||||
@ -47,8 +48,13 @@ export default [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/emprunts",
|
path: "/emprunts",
|
||||||
component: AsyncLoan,
|
component: AsyncLoans,
|
||||||
loadData: loadLoanData,
|
loadData: loadLoansData,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/emprunter",
|
||||||
|
component: AsyncLoaning,
|
||||||
|
loadData: loadLoaningData,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/fiches",
|
path: "/fiches",
|
||||||
|
@ -16,7 +16,7 @@ export const detailedBoxListGet = expressAccessor.get(async (list) => {
|
|||||||
const toBeAsked: DetailedBox[] = []
|
const toBeAsked: DetailedBox[] = []
|
||||||
|
|
||||||
gameList.forEach((game) => {
|
gameList.forEach((game) => {
|
||||||
const box: Box | undefined = list.find((g) => g.gameId === game.id)
|
const box: Box | undefined = list.find((b) => b.gameId === game.id)
|
||||||
if ((box && box.unplayable) || (!box && !game.toBeKnown)) {
|
if ((box && box.unplayable) || (!box && !game.toBeKnown)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,24 @@
|
|||||||
import axios from "axios"
|
import axios from "axios"
|
||||||
import { Parser } from "xml2js"
|
import { Parser } from "xml2js"
|
||||||
import { assign, cloneDeep, find } from "lodash"
|
import {
|
||||||
|
assign,
|
||||||
|
cloneDeep,
|
||||||
|
every,
|
||||||
|
find,
|
||||||
|
groupBy,
|
||||||
|
last,
|
||||||
|
mapValues,
|
||||||
|
reduce,
|
||||||
|
some,
|
||||||
|
sortBy,
|
||||||
|
} from "lodash"
|
||||||
import ExpressAccessors from "./expressAccessors"
|
import ExpressAccessors from "./expressAccessors"
|
||||||
import { Game, GameWithoutId, translationGame } from "../../services/games"
|
import { getSheet } from "./accessors"
|
||||||
|
import { Game, GameWithoutId, GameWithVolunteers, translationGame } from "../../services/games"
|
||||||
|
import { translationVolunteer, Volunteer, VolunteerWithoutId } from "../../services/volunteers"
|
||||||
|
import { translationBox, Box, BoxWithoutId } from "../../services/boxes"
|
||||||
|
import { getUniqueNickname } from "./tools"
|
||||||
|
import { gameTitleCategory, gameTitleExactCategory, gameTitleOrder } from "../../store/utils"
|
||||||
|
|
||||||
const expressAccessor = new ExpressAccessors<GameWithoutId, Game>(
|
const expressAccessor = new ExpressAccessors<GameWithoutId, Game>(
|
||||||
"Games",
|
"Games",
|
||||||
@ -10,7 +26,7 @@ const expressAccessor = new ExpressAccessors<GameWithoutId, Game>(
|
|||||||
translationGame
|
translationGame
|
||||||
)
|
)
|
||||||
|
|
||||||
export const gameListGet = expressAccessor.listGet()
|
// export const gameListGet = expressAccessor.listGet()
|
||||||
// export const gameGet = expressAccessor.get()
|
// export const gameGet = expressAccessor.get()
|
||||||
// export const gameAdd = expressAccessor.add()
|
// export const gameAdd = expressAccessor.add()
|
||||||
// export const gameSet = expressAccessor.set()
|
// export const gameSet = expressAccessor.set()
|
||||||
@ -18,7 +34,7 @@ export const gameListGet = expressAccessor.listGet()
|
|||||||
export const gameDetailsUpdate = expressAccessor.listSet(
|
export const gameDetailsUpdate = expressAccessor.listSet(
|
||||||
async (list, _body, _id, _roles, request) => {
|
async (list, _body, _id, _roles, request) => {
|
||||||
request.setTimeout(500000)
|
request.setTimeout(500000)
|
||||||
const newList = cloneDeep(list)
|
const newList: Game[] = cloneDeep(list)
|
||||||
|
|
||||||
const data = await getData()
|
const data = await getData()
|
||||||
const parser = new Parser()
|
const parser = new Parser()
|
||||||
@ -98,3 +114,214 @@ async function getData(): Promise<string> {
|
|||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const gameListGet = expressAccessor.get(async (list) => {
|
||||||
|
const boxSheet = await getSheet<BoxWithoutId, Box>("Boxes", new Box(), translationBox)
|
||||||
|
|
||||||
|
const boxList = await boxSheet.getList()
|
||||||
|
if (!boxList) {
|
||||||
|
throw Error("Unable to load boxList")
|
||||||
|
}
|
||||||
|
|
||||||
|
const toBeAsked: Game[] = []
|
||||||
|
|
||||||
|
list.forEach((game) => {
|
||||||
|
const box: Box | undefined = boxList.find((b) => b.gameId === game.id)
|
||||||
|
if (!box) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
toBeAsked.push({
|
||||||
|
id: game.id,
|
||||||
|
title: game.title,
|
||||||
|
bggId: game.bggId,
|
||||||
|
bggIdAlternative: game.bggIdAlternative,
|
||||||
|
bggPhoto: game.bggPhoto,
|
||||||
|
poufpaf: game.poufpaf,
|
||||||
|
playersMin: game.playersMin,
|
||||||
|
playersMax: game.playersMax,
|
||||||
|
duration: game.duration,
|
||||||
|
type: game.type,
|
||||||
|
} as Game)
|
||||||
|
})
|
||||||
|
|
||||||
|
return toBeAsked
|
||||||
|
})
|
||||||
|
|
||||||
|
export const gameWithVolunteersListGet = expressAccessor.get(
|
||||||
|
async (list, _body, id): Promise<GameWithVolunteers[]> => {
|
||||||
|
if (id <= 0) {
|
||||||
|
throw Error(`L'accès est réservé aux utilisateurs identifiés`)
|
||||||
|
}
|
||||||
|
const volunteerSheet = await getSheet<VolunteerWithoutId, Volunteer>(
|
||||||
|
"Volunteers",
|
||||||
|
new Volunteer(),
|
||||||
|
translationVolunteer
|
||||||
|
)
|
||||||
|
const volunteerList = await volunteerSheet.getList()
|
||||||
|
if (!volunteerList) {
|
||||||
|
throw Error("Unable to load volunteers")
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentVolunteer: Volunteer | undefined = volunteerList.find((v) => v.id === id)
|
||||||
|
if (!currentVolunteer) {
|
||||||
|
throw Error(`Unknown volunteer ${id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const boxSheet = await getSheet<BoxWithoutId, Box>("Boxes", new Box(), translationBox)
|
||||||
|
const boxList = await boxSheet.getList()
|
||||||
|
if (!boxList) {
|
||||||
|
throw Error("Unable to load boxes")
|
||||||
|
}
|
||||||
|
|
||||||
|
const giftGameIds = list
|
||||||
|
.filter((game) =>
|
||||||
|
every(
|
||||||
|
volunteerList,
|
||||||
|
(v) => !v.loanable.includes(game.id) && !v.playable.includes(game.id)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.map((game) => game.id)
|
||||||
|
|
||||||
|
const gamesToLoan = list
|
||||||
|
.filter((game) => currentVolunteer.loanable.includes(game.id))
|
||||||
|
.map((game) => ({
|
||||||
|
...cloneDeep(game),
|
||||||
|
volunteerNicknames: volunteerToNicknameList(
|
||||||
|
volunteerList.filter((v) => v.loanable.includes(game.id)),
|
||||||
|
volunteerList
|
||||||
|
),
|
||||||
|
toLoan: true,
|
||||||
|
boxCount: boxList.filter((b) => b.gameId === game.id).length,
|
||||||
|
}))
|
||||||
|
|
||||||
|
const gamesToGift = list
|
||||||
|
.filter(
|
||||||
|
(game) =>
|
||||||
|
giftGameIds.includes(game.id) && currentVolunteer.loanable.includes(game.id)
|
||||||
|
)
|
||||||
|
.map((game) => ({
|
||||||
|
...cloneDeep(game),
|
||||||
|
volunteerNicknames: volunteerToNicknameList(
|
||||||
|
volunteerList.filter((v) => v.loanable.includes(game.id)),
|
||||||
|
volunteerList
|
||||||
|
),
|
||||||
|
toLoan: false,
|
||||||
|
boxCount: boxList.filter((b) => b.gameId === game.id).length,
|
||||||
|
}))
|
||||||
|
|
||||||
|
return [...gamesToLoan, ...gamesToGift]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
function volunteerToNicknameList(volunteers: Volunteer[], allVolunteers: Volunteer[]): string[] {
|
||||||
|
return volunteers
|
||||||
|
.map((v) => getUniqueNickname(allVolunteers, v))
|
||||||
|
.sort((a, b) => a.localeCompare(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const gamesToGiveListGet = expressAccessor.get(async (list): Promise<string[]> => {
|
||||||
|
const volunteerSheet = await getSheet<VolunteerWithoutId, Volunteer>(
|
||||||
|
"Volunteers",
|
||||||
|
new Volunteer(),
|
||||||
|
translationVolunteer
|
||||||
|
)
|
||||||
|
const volunteerList = await volunteerSheet.getList()
|
||||||
|
if (!volunteerList) {
|
||||||
|
throw Error("Unable to load volunteers")
|
||||||
|
}
|
||||||
|
|
||||||
|
const boxSheet = await getSheet<BoxWithoutId, Box>("Boxes", new Box(), translationBox)
|
||||||
|
const boxList = await boxSheet.getList()
|
||||||
|
if (!boxList) {
|
||||||
|
throw Error("Unable to load boxes")
|
||||||
|
}
|
||||||
|
|
||||||
|
volunteerList[105].playable = []
|
||||||
|
|
||||||
|
const giftGameTitles = list
|
||||||
|
.filter((game) =>
|
||||||
|
every(
|
||||||
|
volunteerList,
|
||||||
|
(v) => !v.loanable.includes(game.id) && !v.playable.includes(game.id)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.map((game) => game.title)
|
||||||
|
|
||||||
|
return giftGameTitles
|
||||||
|
})
|
||||||
|
type GameCategory = { start: string; end: string; titles: string[] }
|
||||||
|
export const gameTitleOrderCategories = expressAccessor.get(
|
||||||
|
async (list): Promise<GameCategory[]> => {
|
||||||
|
const volunteerSheet = await getSheet<VolunteerWithoutId, Volunteer>(
|
||||||
|
"Volunteers",
|
||||||
|
new Volunteer(),
|
||||||
|
translationVolunteer
|
||||||
|
)
|
||||||
|
const volunteerList = await volunteerSheet.getList()
|
||||||
|
if (!volunteerList) {
|
||||||
|
throw Error("Unable to load volunteers")
|
||||||
|
}
|
||||||
|
|
||||||
|
const boxSheet = await getSheet<BoxWithoutId, Box>("Boxes", new Box(), translationBox)
|
||||||
|
const boxList = await boxSheet.getList()
|
||||||
|
if (!boxList) {
|
||||||
|
throw Error("Unable to load boxes")
|
||||||
|
}
|
||||||
|
|
||||||
|
volunteerList[105].playable = []
|
||||||
|
|
||||||
|
const giftGameIds = list
|
||||||
|
.filter((game) =>
|
||||||
|
every(
|
||||||
|
volunteerList,
|
||||||
|
(v) => !v.loanable.includes(game.id) && !v.playable.includes(game.id)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.map((game) => game.id)
|
||||||
|
|
||||||
|
const gamesToLoan = sortBy(
|
||||||
|
list.filter((game) => !giftGameIds.includes(game.id)).map((game) => cloneDeep(game)),
|
||||||
|
gameTitleOrder
|
||||||
|
)
|
||||||
|
|
||||||
|
const exaustiveCats = mapValues(groupBy(gamesToLoan, gameTitleCategory), (gameList) =>
|
||||||
|
gameList.map((game) => game.title)
|
||||||
|
)
|
||||||
|
|
||||||
|
const cats: GameCategory[] = reduce(
|
||||||
|
Object.entries(exaustiveCats),
|
||||||
|
(res, [exaustiveCat, catTitles]) => {
|
||||||
|
const prevCat = last(res)
|
||||||
|
const prevCount = prevCat?.titles.length || 0
|
||||||
|
if (prevCount === 0 || prevCount + catTitles.length > 10) {
|
||||||
|
const newCat = {
|
||||||
|
start: exaustiveCat,
|
||||||
|
end: exaustiveCat,
|
||||||
|
titles: [...catTitles],
|
||||||
|
}
|
||||||
|
res.push(newCat)
|
||||||
|
} else if (prevCat !== undefined) {
|
||||||
|
prevCat.end = exaustiveCat
|
||||||
|
catTitles.forEach((title) => prevCat.titles.push(title))
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
[] as GameCategory[]
|
||||||
|
)
|
||||||
|
|
||||||
|
const catList: string[] = cats.map((cat) => `${cat.start}-${cat.end}`)
|
||||||
|
|
||||||
|
console.log(catList.join("\n"))
|
||||||
|
|
||||||
|
const availableGiftIds = giftGameIds.filter((id) =>
|
||||||
|
some(volunteerList, (v: Volunteer) => v.giftable.includes(id))
|
||||||
|
)
|
||||||
|
console.log("availableGiftIds", availableGiftIds)
|
||||||
|
|
||||||
|
gamesToLoan.forEach((g) => gameTitleExactCategory(g))
|
||||||
|
|
||||||
|
return cats
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// _.mapValues(list, cat => cat.length)
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
// eslint-disable-next-line max-classes-per-file
|
// eslint-disable-next-line max-classes-per-file
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import _ from "lodash"
|
import _, { assign } from "lodash"
|
||||||
import { promises as fs } from "fs"
|
import { promises as fs } from "fs"
|
||||||
import { Volunteer } from "../../services/volunteers"
|
import { Volunteer } from "../../services/volunteers"
|
||||||
import { Postulant } from "../../services/postulants"
|
import { Postulant } from "../../services/postulants"
|
||||||
|
import { Retex } from "../../services/retex"
|
||||||
|
|
||||||
const DB_PATH = path.resolve(process.cwd(), "access/db.json")
|
const DB_PATH = path.resolve(process.cwd(), "access/db.json")
|
||||||
const DB_TO_LOAD_PATH = path.resolve(process.cwd(), "access/dbToLoad.json")
|
const DB_TO_LOAD_PATH = path.resolve(process.cwd(), "access/dbToLoad.json")
|
||||||
@ -275,6 +276,11 @@ function anonimizedDb(_s: States): States {
|
|||||||
v.comment = v.id % 3 === 0 ? "Bonjour, j'adore l'initiative!" : ""
|
v.comment = v.id % 3 === 0 ? "Bonjour, j'adore l'initiative!" : ""
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if (s.Retex) {
|
||||||
|
;(s.Retex as Retex[]).forEach((r) => {
|
||||||
|
assign(r, new Retex(), { id: r.id, dayWishes: r.dayWishes })
|
||||||
|
})
|
||||||
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
16
src/server/gsheets/tools.ts
Normal file
16
src/server/gsheets/tools.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { remove } from "lodash"
|
||||||
|
import { Volunteer } from "../../services/volunteers"
|
||||||
|
|
||||||
|
export function getUniqueNickname(list: Volunteer[], volunteer: Volunteer): string {
|
||||||
|
const lastnameList = list
|
||||||
|
.filter((v) => v.firstname === volunteer.firstname)
|
||||||
|
.map((v) => v.lastname)
|
||||||
|
let lastnamePrefix = ""
|
||||||
|
while (lastnameList.length > 1) {
|
||||||
|
lastnamePrefix += volunteer.lastname.charAt(lastnamePrefix.length)
|
||||||
|
// eslint-disable-next-line no-loop-func
|
||||||
|
remove(lastnameList, (lastname) => !lastname.startsWith(lastnamePrefix))
|
||||||
|
}
|
||||||
|
const nickname = `${volunteer.firstname}${lastnamePrefix ? ` ${lastnamePrefix}.` : ""}`
|
||||||
|
return nickname
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import path from "path"
|
import path from "path"
|
||||||
import * as fs from "fs"
|
import * as fs from "fs"
|
||||||
import { assign, cloneDeep, max, omit, pick, remove } from "lodash"
|
import { assign, cloneDeep, max, omit, pick } from "lodash"
|
||||||
import bcrypt from "bcrypt"
|
import bcrypt from "bcrypt"
|
||||||
import sgMail from "@sendgrid/mail"
|
import sgMail from "@sendgrid/mail"
|
||||||
|
|
||||||
@ -24,6 +24,7 @@ import {
|
|||||||
} from "../../services/volunteers"
|
} from "../../services/volunteers"
|
||||||
import { canonicalEmail, canonicalMobile, trim, validMobile } from "../../utils/standardization"
|
import { canonicalEmail, canonicalMobile, trim, validMobile } from "../../utils/standardization"
|
||||||
import { getJwt } from "../secure"
|
import { getJwt } from "../secure"
|
||||||
|
import { getUniqueNickname } from "./tools"
|
||||||
|
|
||||||
const expressAccessor = new ExpressAccessors<VolunteerWithoutId, Volunteer>(
|
const expressAccessor = new ExpressAccessors<VolunteerWithoutId, Volunteer>(
|
||||||
"Volunteers",
|
"Volunteers",
|
||||||
@ -577,17 +578,3 @@ export const volunteerLoanSet = expressAccessor.set(async (list, body, id) => {
|
|||||||
} as VolunteerLoan,
|
} as VolunteerLoan,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
function getUniqueNickname(list: Volunteer[], volunteer: Volunteer): string {
|
|
||||||
const lastnameList = list
|
|
||||||
.filter((v) => v.firstname === volunteer.firstname)
|
|
||||||
.map((v) => v.lastname)
|
|
||||||
let lastnamePrefix = ""
|
|
||||||
while (lastnameList.length > 1) {
|
|
||||||
lastnamePrefix += volunteer.lastname.charAt(lastnamePrefix.length)
|
|
||||||
// eslint-disable-next-line no-loop-func
|
|
||||||
remove(lastnameList, (lastname) => !lastname.startsWith(lastnamePrefix))
|
|
||||||
}
|
|
||||||
const nickname = `${volunteer.firstname}${lastnamePrefix ? ` ${lastnamePrefix}.` : ""}`
|
|
||||||
return nickname
|
|
||||||
}
|
|
||||||
|
@ -19,7 +19,13 @@ import certbotRouter from "../routes/certbot"
|
|||||||
import { hasSecret, secure } from "./secure"
|
import { hasSecret, secure } from "./secure"
|
||||||
import { announcementListGet } from "./gsheets/announcements"
|
import { announcementListGet } from "./gsheets/announcements"
|
||||||
import { detailedBoxListGet } from "./gsheets/boxes"
|
import { detailedBoxListGet } from "./gsheets/boxes"
|
||||||
import { gameListGet, gameDetailsUpdate } from "./gsheets/games"
|
import {
|
||||||
|
gameListGet,
|
||||||
|
gameDetailsUpdate,
|
||||||
|
gameWithVolunteersListGet,
|
||||||
|
gamesToGiveListGet,
|
||||||
|
gameTitleOrderCategories,
|
||||||
|
} from "./gsheets/games"
|
||||||
import { postulantAdd } from "./gsheets/postulants"
|
import { postulantAdd } from "./gsheets/postulants"
|
||||||
import { teamListGet } from "./gsheets/teams"
|
import { teamListGet } from "./gsheets/teams"
|
||||||
import {
|
import {
|
||||||
@ -98,6 +104,8 @@ app.get(
|
|||||||
app.get("/GameDetailsUpdate", gameDetailsUpdate)
|
app.get("/GameDetailsUpdate", gameDetailsUpdate)
|
||||||
app.get("/BoxDetailedListGet", detailedBoxListGet)
|
app.get("/BoxDetailedListGet", detailedBoxListGet)
|
||||||
app.get("/GameListGet", gameListGet)
|
app.get("/GameListGet", gameListGet)
|
||||||
|
app.get("/GamesToGiveListGet", gamesToGiveListGet)
|
||||||
|
app.get("/GameTitleOrderCategories", gameTitleOrderCategories)
|
||||||
app.get("/MiscMeetingDateListGet", miscMeetingDateListGet)
|
app.get("/MiscMeetingDateListGet", miscMeetingDateListGet)
|
||||||
app.get("/WishListGet", wishListGet)
|
app.get("/WishListGet", wishListGet)
|
||||||
app.post("/WishAdd", wishAdd)
|
app.post("/WishAdd", wishAdd)
|
||||||
@ -109,6 +117,7 @@ app.get("/VolunteerListGet", secure as RequestHandler, volunteerListGet)
|
|||||||
|
|
||||||
// Secured APIs
|
// Secured APIs
|
||||||
app.get("/AnnouncementListGet", secure as RequestHandler, announcementListGet)
|
app.get("/AnnouncementListGet", secure as RequestHandler, announcementListGet)
|
||||||
|
app.get("/GameWithVolunteersListGet", secure as RequestHandler, gameWithVolunteersListGet)
|
||||||
app.get("/MiscDiscordInvitationGet", secure as RequestHandler, miscDiscordInvitation)
|
app.get("/MiscDiscordInvitationGet", secure as RequestHandler, miscDiscordInvitation)
|
||||||
app.post("/RetexSet", secure as RequestHandler, retexSet)
|
app.post("/RetexSet", secure as RequestHandler, retexSet)
|
||||||
app.get("/TeamListGet", teamListGet)
|
app.get("/TeamListGet", teamListGet)
|
||||||
|
@ -90,6 +90,37 @@ export default class ServiceAccessors<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
securedCustomListGet<InputElements extends Array<any>, OutputType>(
|
||||||
|
apiName: string
|
||||||
|
): (
|
||||||
|
jwt: string,
|
||||||
|
...params: InputElements
|
||||||
|
) => Promise<{
|
||||||
|
data?: any
|
||||||
|
error?: Error
|
||||||
|
}> {
|
||||||
|
interface ElementGetResponse {
|
||||||
|
data?: any
|
||||||
|
error?: Error
|
||||||
|
}
|
||||||
|
return async (jwt: string, ...params: InputElements): Promise<ElementGetResponse> => {
|
||||||
|
try {
|
||||||
|
const auth = { headers: { Authorization: `Bearer ${jwt}` } }
|
||||||
|
const fullAxiosConfig = _.defaultsDeep(auth, axiosConfig)
|
||||||
|
const { data } = await axios.get(
|
||||||
|
`${config.API_URL}/${this.elementName}${apiName}`,
|
||||||
|
{ ...fullAxiosConfig, params }
|
||||||
|
)
|
||||||
|
if (data.error) {
|
||||||
|
throw Error(data.error)
|
||||||
|
}
|
||||||
|
return { data } as { data: OutputType }
|
||||||
|
} catch (error) {
|
||||||
|
return { error: error as Error }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
add(): (volunteerWithoutId: ElementNoId) => Promise<{
|
add(): (volunteerWithoutId: ElementNoId) => Promise<{
|
||||||
data?: Element
|
data?: Element
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable max-classes-per-file */
|
||||||
export class Game {
|
export class Game {
|
||||||
id = 0
|
id = 0
|
||||||
|
|
||||||
@ -26,6 +27,14 @@ export class Game {
|
|||||||
toBeKnown = false
|
toBeKnown = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class GameWithVolunteers extends Game {
|
||||||
|
volunteerNicknames: string[] = []
|
||||||
|
|
||||||
|
toLoan = true
|
||||||
|
|
||||||
|
boxCount = 0
|
||||||
|
}
|
||||||
|
|
||||||
export const translationGame: { [k in keyof Game]: string } = {
|
export const translationGame: { [k in keyof Game]: string } = {
|
||||||
id: "id",
|
id: "id",
|
||||||
title: "titre",
|
title: "titre",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import ServiceAccessors from "./accessors"
|
import ServiceAccessors from "./accessors"
|
||||||
import { elementName, Game, GameWithoutId } from "./games"
|
import { elementName, Game, GameWithVolunteers, GameWithoutId } from "./games"
|
||||||
|
|
||||||
const serviceAccessors = new ServiceAccessors<GameWithoutId, Game>(elementName)
|
const serviceAccessors = new ServiceAccessors<GameWithoutId, Game>(elementName)
|
||||||
|
|
||||||
@ -9,3 +9,8 @@ export const gameListGet = serviceAccessors.listGet()
|
|||||||
// export const gameSet = serviceAccessors.set()
|
// export const gameSet = serviceAccessors.set()
|
||||||
|
|
||||||
export const gameDetailsUpdate = serviceAccessors.securedCustomGet<[], Game[]>("DetailsUpdate")
|
export const gameDetailsUpdate = serviceAccessors.securedCustomGet<[], Game[]>("DetailsUpdate")
|
||||||
|
|
||||||
|
export const gameWithVolunteersListGet = serviceAccessors.securedCustomListGet<
|
||||||
|
[],
|
||||||
|
GameWithVolunteers[]
|
||||||
|
>("WithVolunteersListGet")
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { PayloadAction, createSlice, createEntityAdapter, createSelector } from "@reduxjs/toolkit"
|
import { PayloadAction, createSlice, createEntityAdapter, createSelector } from "@reduxjs/toolkit"
|
||||||
import { sortedUniqBy, sortBy } from "lodash"
|
import { sortedUniqBy, sortBy } from "lodash"
|
||||||
|
|
||||||
import { StateRequest, toastError, elementListFetch } from "./utils"
|
import { StateRequest, toastError, elementListFetch, gameTitleOrder } from "./utils"
|
||||||
import { DetailedBox } from "../services/boxes"
|
import { DetailedBox } from "../services/boxes"
|
||||||
import { AppThunk, AppState, EntitiesRequest } from "."
|
import { AppThunk, AppState, EntitiesRequest } from "."
|
||||||
import { detailedBoxListGet } from "../services/boxesAccessors"
|
import { detailedBoxListGet } from "../services/boxesAccessors"
|
||||||
@ -59,9 +59,10 @@ export const selectBoxList = createSelector(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
export const selectSortedUniqueDetailedBoxes = createSelector(selectBoxList, (boxes) =>
|
export const selectSortedUniqueDetailedBoxes = createSelector(selectBoxList, (boxes) => {
|
||||||
sortedUniqBy(sortBy(boxes, "title"), "title")
|
const validBoxes = boxes.filter((box) => box) as DetailedBox[]
|
||||||
)
|
return sortedUniqBy(sortBy(validBoxes, gameTitleOrder), gameTitleOrder)
|
||||||
|
})
|
||||||
|
|
||||||
export const selectContainerSortedDetailedBoxes = createSelector(selectBoxList, (boxes) =>
|
export const selectContainerSortedDetailedBoxes = createSelector(selectBoxList, (boxes) =>
|
||||||
sortBy(boxes, "container")
|
sortBy(boxes, "container")
|
||||||
|
73
src/store/gameWithVolunteersList.ts
Normal file
73
src/store/gameWithVolunteersList.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import { PayloadAction, createSlice, createEntityAdapter, createSelector } from "@reduxjs/toolkit"
|
||||||
|
import { sortedUniqBy, sortBy } from "lodash"
|
||||||
|
|
||||||
|
import { StateRequest, toastError, elementListFetch, gameTitleOrder } from "./utils"
|
||||||
|
import { GameWithVolunteers } from "../services/games"
|
||||||
|
import { AppThunk, AppState, EntitiesRequest } from "."
|
||||||
|
import { gameWithVolunteersListGet } from "../services/gamesAccessors"
|
||||||
|
|
||||||
|
const gameAdapter = createEntityAdapter<GameWithVolunteers>()
|
||||||
|
|
||||||
|
export const initialState = gameAdapter.getInitialState({
|
||||||
|
readyStatus: "idle",
|
||||||
|
} as StateRequest)
|
||||||
|
|
||||||
|
const gameWithVolunteersList = createSlice({
|
||||||
|
name: "gameWithVolunteersList",
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
getRequesting: (state) => {
|
||||||
|
state.readyStatus = "request"
|
||||||
|
},
|
||||||
|
getSuccess: (state, { payload }: PayloadAction<GameWithVolunteers[]>) => {
|
||||||
|
state.readyStatus = "success"
|
||||||
|
gameAdapter.setAll(state, payload)
|
||||||
|
},
|
||||||
|
getFailure: (state, { payload }: PayloadAction<string>) => {
|
||||||
|
state.readyStatus = "failure"
|
||||||
|
state.error = payload
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default gameWithVolunteersList.reducer
|
||||||
|
export const { getRequesting, getSuccess, getFailure } = gameWithVolunteersList.actions
|
||||||
|
|
||||||
|
export const fetchGameWithVolunteersList = elementListFetch(
|
||||||
|
gameWithVolunteersListGet,
|
||||||
|
getRequesting,
|
||||||
|
getSuccess,
|
||||||
|
getFailure,
|
||||||
|
(error: Error) => toastError(`Erreur lors du chargement des jeux JAV: ${error.message}`)
|
||||||
|
)
|
||||||
|
|
||||||
|
const shouldFetchGameWithVolunteersList = (state: AppState) =>
|
||||||
|
state.gameWithVolunteersList.readyStatus !== "success"
|
||||||
|
|
||||||
|
export const fetchGameWithVolunteersListIfNeed = (): AppThunk => (dispatch, getState) => {
|
||||||
|
const { jwt } = getState().auth
|
||||||
|
if (shouldFetchGameWithVolunteersList(getState()))
|
||||||
|
return dispatch(fetchGameWithVolunteersList(jwt))
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const selectGameWithVolunteersListState = (
|
||||||
|
state: AppState
|
||||||
|
): EntitiesRequest<GameWithVolunteers> => state.gameWithVolunteersList
|
||||||
|
|
||||||
|
export const selectGameWithVolunteersList = createSelector(
|
||||||
|
selectGameWithVolunteersListState,
|
||||||
|
({ ids, entities, readyStatus }) => {
|
||||||
|
if (readyStatus !== "success") return []
|
||||||
|
return ids.map((id) => entities[id])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectSortedUniqueGamesWithVolunteers = createSelector(
|
||||||
|
selectGameWithVolunteersList,
|
||||||
|
(games) => {
|
||||||
|
const gameWithVolunteers = games.filter((game) => game) as GameWithVolunteers[]
|
||||||
|
return sortedUniqBy(sortBy(gameWithVolunteers, gameTitleOrder), gameTitleOrder)
|
||||||
|
}
|
||||||
|
)
|
@ -5,6 +5,7 @@ import announcementList from "./announcementList"
|
|||||||
import auth from "./auth"
|
import auth from "./auth"
|
||||||
import boxList from "./boxList"
|
import boxList from "./boxList"
|
||||||
import gameList from "./gameList"
|
import gameList from "./gameList"
|
||||||
|
import gameWithVolunteersList from "./gameWithVolunteersList"
|
||||||
import gameDetailsUpdate from "./gameDetailsUpdate"
|
import gameDetailsUpdate from "./gameDetailsUpdate"
|
||||||
import miscDiscordInvitation from "./miscDiscordInvitation"
|
import miscDiscordInvitation from "./miscDiscordInvitation"
|
||||||
import miscMeetingDateList from "./miscMeetingDateList"
|
import miscMeetingDateList from "./miscMeetingDateList"
|
||||||
@ -39,6 +40,7 @@ export default (history: History) => ({
|
|||||||
auth,
|
auth,
|
||||||
boxList,
|
boxList,
|
||||||
gameList,
|
gameList,
|
||||||
|
gameWithVolunteersList,
|
||||||
gameDetailsUpdate,
|
gameDetailsUpdate,
|
||||||
miscDiscordInvitation,
|
miscDiscordInvitation,
|
||||||
miscMeetingDateList,
|
miscMeetingDateList,
|
||||||
|
@ -4,6 +4,7 @@ import {
|
|||||||
ActionCreatorWithPayload,
|
ActionCreatorWithPayload,
|
||||||
ThunkDispatch,
|
ThunkDispatch,
|
||||||
} from "@reduxjs/toolkit"
|
} from "@reduxjs/toolkit"
|
||||||
|
import { find } from "lodash"
|
||||||
import { toast } from "react-toastify"
|
import { toast } from "react-toastify"
|
||||||
|
|
||||||
import { AppState, AppThunk } from "."
|
import { AppState, AppThunk } from "."
|
||||||
@ -143,3 +144,129 @@ export function elementValueFetch<Element>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function gameTitleOrder(boxOrGame: { title: string }): string {
|
||||||
|
return boxOrGame.title
|
||||||
|
.normalize("NFD")
|
||||||
|
.replace(/[\u0300-\u036f]/g, "")
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/^[^a-z0-9]+/, "")
|
||||||
|
.replace(/^(le |la |les |l'|the )/, "")
|
||||||
|
.replace(/[^a-z0-9]/g, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
export function gameTitleCategory(boxOrGame: { title: string }, length = 3): string {
|
||||||
|
return gameTitleOrder(boxOrGame)
|
||||||
|
.substring(0, length)
|
||||||
|
.replace(/(?<=[0-9]).+/, "")
|
||||||
|
.toUpperCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function gameTitleExactCategory(boxOrGame: { title: string }): string {
|
||||||
|
const cats = [
|
||||||
|
["1", "6"],
|
||||||
|
["7", "AB"],
|
||||||
|
["AC", "AK"],
|
||||||
|
["ALA", "ALO"],
|
||||||
|
["ALP", "AP"],
|
||||||
|
["AQ", "ARG"],
|
||||||
|
["ARH", "AT"],
|
||||||
|
["AU", "AV"],
|
||||||
|
["AW", "BAR"],
|
||||||
|
["BAS", "BER"],
|
||||||
|
["BES", "BLI"],
|
||||||
|
["BLJ", "BRE"],
|
||||||
|
["BRF", "CAC"],
|
||||||
|
["CAD", "CAP"],
|
||||||
|
["CAQ", "CEM"],
|
||||||
|
["CEN", "CHIN"],
|
||||||
|
["CHJ", "CK"],
|
||||||
|
["CL"],
|
||||||
|
["COA", "COL"],
|
||||||
|
["COM"],
|
||||||
|
["CON", "COP"],
|
||||||
|
["COQ", "CRA"],
|
||||||
|
["CRB", "DEB"],
|
||||||
|
["DEC", "DES"],
|
||||||
|
["DET", "DIC"],
|
||||||
|
["DID", "DOM"],
|
||||||
|
["DON", "DR"],
|
||||||
|
["DS", "ELG"],
|
||||||
|
["ELH", "EVE"],
|
||||||
|
["EVD", "FAM"],
|
||||||
|
["FAN", "FIC"],
|
||||||
|
["FID", "FOL"],
|
||||||
|
["FOM", "FUM"],
|
||||||
|
["FUN", "GEE"],
|
||||||
|
["GED", "GLA"],
|
||||||
|
["GLB", "GQ"],
|
||||||
|
["GR", "HAN"],
|
||||||
|
["HAO", "HIM"],
|
||||||
|
["HIN", "IL"],
|
||||||
|
["IM", "IS"],
|
||||||
|
["IT", "JE"],
|
||||||
|
["JD", "KAB"],
|
||||||
|
["KAC", "KEM"],
|
||||||
|
["KEN", "KILL"],
|
||||||
|
["KIM", "KOD"],
|
||||||
|
["KOE", "LAB"],
|
||||||
|
["LAC", "LIC"],
|
||||||
|
["LID", "LIP"],
|
||||||
|
["LIQ", "LOQ"],
|
||||||
|
["LOR", "LZ"],
|
||||||
|
["MAA", "MAN"],
|
||||||
|
["MAO", "MED"],
|
||||||
|
["MEE", "MIM"],
|
||||||
|
["MINE", "MOR"],
|
||||||
|
["MOS", "MYS"],
|
||||||
|
["MYT", "NE"],
|
||||||
|
["ND", "ODD"],
|
||||||
|
["ODE", "ONE"],
|
||||||
|
["ONF", "OU"],
|
||||||
|
["OV", "PAN"],
|
||||||
|
["PAO", "PEL"],
|
||||||
|
["PEM", "PIO"],
|
||||||
|
["PIP", "PLZ"],
|
||||||
|
["PO", "PRI"],
|
||||||
|
["PRJ", "PZ"],
|
||||||
|
["QI", "RAT"],
|
||||||
|
["RAU", "RI"],
|
||||||
|
["RJ", "ROO"],
|
||||||
|
["ROP", "SAL"],
|
||||||
|
["SAM", "SEI"],
|
||||||
|
["SEJ", "SIC"],
|
||||||
|
["SID", "SL"],
|
||||||
|
["SM", "SO"],
|
||||||
|
["SPA", "SPX"],
|
||||||
|
["SPY", "STE"],
|
||||||
|
["STD", "SUN"],
|
||||||
|
["SUO", "TAF"],
|
||||||
|
["TAG", "TD"],
|
||||||
|
["TE", "TIC"],
|
||||||
|
["TID", "TIL"],
|
||||||
|
["TIM"],
|
||||||
|
["TIN", "TO"],
|
||||||
|
["TP", "TRO"],
|
||||||
|
["TRP", "UNE"],
|
||||||
|
["UND", "VEM"],
|
||||||
|
["VEN", "WAT"],
|
||||||
|
["WAU", "WH"],
|
||||||
|
["WI", "YE"],
|
||||||
|
["YOGA", "ZOO"],
|
||||||
|
]
|
||||||
|
|
||||||
|
const gameCat = gameTitleCategory(boxOrGame, 4)
|
||||||
|
|
||||||
|
const foundCat = find(cats, (cat) =>
|
||||||
|
cat[1]
|
||||||
|
? gameCat.substring(0, cat[0].length) >= cat[0] &&
|
||||||
|
gameCat.substring(0, cat[1].length) <= `${cat[1]}`
|
||||||
|
: gameCat.substring(0, cat[0].length) === cat[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!foundCat) {
|
||||||
|
console.log("no game found for ", foundCat, boxOrGame.title)
|
||||||
|
}
|
||||||
|
|
||||||
|
return foundCat ? `${foundCat[0]}-${foundCat[1]}` : gameCat
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user