Add /emprunts and better BGG games parsing
BIN
src/app/img/gameImages/1000.jpg
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
src/app/img/gameImages/1001.png
Normal file
After Width: | Height: | Size: 57 KiB |
BIN
src/app/img/gameImages/1002.jpg
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
src/app/img/gameImages/1003.jpg
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
src/app/img/gameImages/1004.png
Normal file
After Width: | Height: | Size: 53 KiB |
BIN
src/app/img/gameImages/1005.png
Normal file
After Width: | Height: | Size: 61 KiB |
BIN
src/app/img/gameImages/1007.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
src/app/img/gameImages/1008.png
Normal file
After Width: | Height: | Size: 68 KiB |
BIN
src/app/img/gameImages/1009.png
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
src/app/img/gameImages/1010.jpg
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
src/app/img/gameImages/1011.jpg
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
src/app/img/gameImages/1012.jpg
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
src/app/img/gameImages/1013.jpg
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
src/app/img/gameImages/1014.jpg
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
src/app/img/gameImages/1015.jpg
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
src/app/img/gameImages/1016.jpg
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
src/app/img/gameImages/1017.jpg
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
src/app/img/gameImages/1018.jpg
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
src/app/img/gameImages/1019.jpg
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
src/app/img/gameImages/1020.jpg
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
src/app/img/gameImages/1021.jpg
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
src/app/img/gameImages/1022.jpg
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
src/app/img/gameImages/1023.jpg
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
src/app/img/gameImages/1024.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
src/app/img/gameImages/1025.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
src/app/img/gameImages/1026.jpg
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
src/app/img/gameImages/1027.jpg
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
src/app/img/gameImages/1028.jpg
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
src/app/img/gameImages/1029.jpeg
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
src/app/img/gameImages/1030.jpeg
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
src/app/img/gameImages/1031.jpg
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
src/app/img/gameImages/1032.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
src/app/img/gameImages/371.png
Normal file
After Width: | Height: | Size: 55 KiB |
BIN
src/app/img/gameImages/888.jpg
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
src/app/img/gameImages/918.jpg
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
src/app/img/gameImages/992.jpg
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
src/app/img/gameImages/993.jpg
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
src/app/img/gameImages/994.png
Normal file
After Width: | Height: | Size: 57 KiB |
BIN
src/app/img/gameImages/995.jpg
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
src/app/img/gameImages/996.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
src/app/img/gameImages/997.jpg
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
src/app/img/gameImages/998.jpg
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
src/app/img/gameImages/999.png
Normal file
After Width: | Height: | Size: 36 KiB |
12
src/app/img/giftable.svg
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg version="1.1" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g transform="matrix(1.638 0 0 1.638 -14.93 -26.05)" fill-rule="evenodd" stroke-width="1.084">
|
||||||
|
<path d="m257.5 107.7h-62.31l22.29-17.88-12.77-16.98-19.95 14.64 9.175-29.63-19.95-6.169-10.38 31.43-10.35-31.43-19.99 6.169 9.174 29.63-20.69-14.91-12.77 16.98 23.03 18.15h-60.01v182.9h185.5v-182.9z"/>
|
||||||
|
<g fill="#FB8">
|
||||||
|
<path d="m240.1 129.4h-66.02v29.01h66.02v-29.01z"/>
|
||||||
|
<path d="m152.2 129.4h-60.63v29.01h60.63v-29.01z"/>
|
||||||
|
<path d="m240.1 179.8h-66.02v89.41h66.02v-89.41z"/>
|
||||||
|
<path d="m152.2 179.8h-60.63v89.41h60.63v-89.41z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 675 B |
7
src/app/img/loanable small.svg
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<svg width="147" height="147" version="1.1" viewBox="0 0 1581 1581" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m320.7 462.6c14.74 7.684 29.66 15.05 44.46 22.62-68.16 285.7-63 591.2 73.48 842.6-9.222-82.36-22.51-23.56 7.917-126.5 80.94 15.62 53.63 4.685 109.3 38.32-101.5-216.1-124.1-468.1-59.85-688.1 14.94 7.422 29.65 15.29 44.69 22.46 4.471-97.97 19.95-192.1 33.81-314-105.1 81.69-178.1 123.4-253.8 202.5z"/>
|
||||||
|
<path d="m1339 1112c-14.74-7.684-29.66-15.05-44.46-22.62 68.16-285.7 63-591.2-73.48-842.6 9.222 82.36 22.51 23.56-7.917 126.5-80.94-15.62-53.63-4.685-109.3-38.32 101.5 216.1 124.1 468.1 59.85 688.1-14.94-7.422-29.65-15.29-44.69-22.46-4.471 97.97-19.95 192.1-33.81 314 105.1-81.69 178.1-123.4 253.8-202.5z"/>
|
||||||
|
<g transform="matrix(1.637,0,0,1.492,1191,889)">
|
||||||
|
<path d="m-362.9 294.4-222.1 1.919-0.7906 106 222.1-1.919z" fill="#fff" fill-rule="evenodd" stroke-width="1.084"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 897 B |
4
src/app/img/loanable.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg version="1.1" viewBox="0 0 1647 1647" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m320.7 462.6c14.74 7.684 29.66 15.05 44.46 22.62-68.16 285.7-63 591.2 73.48 842.6-9.222-82.36-22.51-23.56 7.917-126.5 80.94 15.62 53.63 4.685 109.3 38.32-101.5-216.1-124.1-468.1-59.85-688.1 14.94 7.422 29.65 15.29 44.69 22.46 4.471-97.97 19.95-192.1 33.81-314-105.1 81.69-178.1 123.4-253.8 202.5z"/>
|
||||||
|
<path d="m1339 1112c-14.74-7.684-29.66-15.05-44.46-22.62 68.16-285.7 63-591.2-73.48-842.6 9.222 82.36 22.51 23.56-7.917 126.5-80.94-15.62-53.63-4.685-109.3-38.32 101.5 216.1 124.1 468.1 59.85 688.1-14.94-7.422-29.65-15.29-44.69-22.46-4.471 97.97-19.95 192.1-33.81 314 105.1-81.69 178.1-123.4 253.8-202.5z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 703 B |
4
src/app/img/noOpinion.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="20.98mm" height="18mm" version="1.1" viewBox="0 0 20.98 18" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<ellipse cx="10.64" cy="4.959" rx="1.664" ry="1.643" fill-opacity=".9853" stroke-width=".999"/>
|
||||||
|
<path class="UnoptimicedTransforms" transform="matrix(1.713 0 0 1.713 -7.382 .08839)" d="m9.777 3.951c-0.2838-0.07088-0.5598-0.04619-0.6816 0.2852l-0.6074 1.928-0.373-1.035 0.1504-0.5781c-0.5512 0.6313-1.322 0.4093-1.711 0.793l1.229-0.01562 0.6211 1.76 0.8535-1.68-0.1484 3.264 1.396-0.1914 1.396 0.1914-0.1484-3.264 0.8535 1.68 0.6211-1.76 1.229 0.01562c-0.3885-0.3837-1.16-0.1617-1.711-0.793l0.1504 0.5781-0.373 1.035-0.6074-1.928c-0.2437-0.6627-1.104-0.09524-1.41-0.001953-0.1532-0.04664-0.4448-0.2123-0.7285-0.2832z" fill-opacity=".9853"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 755 B |
13
src/app/img/playable.svg
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<svg width="20.98mm" height="18mm" version="1.1" viewBox="0 0 20.98 18" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g transform="translate(-22.63 -20.84)" fill-opacity=".9853">
|
||||||
|
<g>
|
||||||
|
<ellipse cx="25.95" cy="24.37" rx="1.666" ry="1.645"/>
|
||||||
|
<path d="m26.21 26.12c-0.9445 0.02935-1.688 0.3129-1.682 1.02 0.0094 1.2-0.6131 5.638 0.1249 6.621 0.6255 0.8335 2.379 0.6755 2.899 0.6098 0.03165 0.0086 0.06436 0.01458 0.09883 0.01458h1.666v2.54c0 0.2538 0.1488 0.4582 0.3334 0.4582h1.249c0.1845 0 0.3334-0.2044 0.3334-0.4582v-4.081c0-0.2076-0.1672-0.3748-0.3748-0.3748h-3.156c0.01777-0.8546 0.04491-1.887 0.04281-2.862l2.096 0.6786c0.1253 0.04057 0.2764-0.08329 0.3393-0.2774l0.1835-0.567c0.06285-0.1941 0.01278-0.383-0.1125-0.4235l-2.539-0.8221c-0.03974-0.7396-0.1192-1.342-0.2692-1.639-0.1618-0.3206-0.6662-0.4548-1.233-0.4372z"/>
|
||||||
|
<path d="m40.8 26.12c0.9445 0.02936 1.812 0.4794 1.807 1.186-0.0094 1.2 0.4881 5.471-0.2498 6.454-0.6255 0.8335-2.379 0.6755-2.899 0.6098-0.03166 0.0086-0.06437 0.01458-0.09883 0.01458h-1.666v2.54c0 0.2538-0.1488 0.4582-0.3334 0.4582h-1.249c-0.1845 0-0.3334-0.2044-0.3334-0.4582v-4.081c0-0.2076 0.1672-0.3748 0.3748-0.3748h3.156c-0.01776-0.8546-0.04491-1.887-0.04282-2.862l-2.096 0.6786c-0.1253 0.04057-0.2764-0.08329-0.3393-0.2774l-0.1835-0.567c-0.06286-0.1941-0.01278-0.383 0.1125-0.4235l2.539-0.8221c0.03974-0.7396 0.1192-1.342 0.2692-1.639 0.1618-0.3206 0.6662-0.4548 1.233-0.4372z"/>
|
||||||
|
<ellipse cx="41.05" cy="24.37" rx="1.666" ry="1.645"/>
|
||||||
|
</g>
|
||||||
|
<ellipse cx="33.17" cy="23.21" rx="1.666" ry="1.645" fill="#909090"/>
|
||||||
|
<path d="m33.14 24.98c-0.8121 0-1.479 0.6306-1.602 1.459h-0.02187v3.466h3.581v-3.466h-0.02232c-0.1225-0.8287-0.7896-1.459-1.602-1.459z" fill="#909090"/>
|
||||||
|
<path d="m29.17 30.39c-0.3345 0-0.6039 0.2694-0.6039 0.6039 0 0.3345 0.2694 0.6034 0.6039 0.6034h2.731c-0.02857 0.1133-0.04555 0.2314-0.04555 0.3539v4.04c0 0.7957 0.6407 1.436 1.436 1.436h0.2505c0.7957 0 1.436-0.6403 1.436-1.436v-4.04c0-0.1224-0.01653-0.2405-0.04509-0.3539h2.606c0.3345 0 0.6039-0.269 0.6039-0.6034 0-0.3345-0.2694-0.6039-0.6039-0.6039z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
@ -8,7 +8,7 @@ import {
|
|||||||
useVolunteerKnowledge,
|
useVolunteerKnowledge,
|
||||||
} from "../../store/volunteerKnowledgeSet"
|
} from "../../store/volunteerKnowledgeSet"
|
||||||
|
|
||||||
const BoxList: React.FC = (): JSX.Element | null => {
|
const KnowledgeBoxList: React.FC = (): JSX.Element | null => {
|
||||||
const detailedBoxes = useSelector(selectSortedUniqueDetailedBoxes)
|
const detailedBoxes = useSelector(selectSortedUniqueDetailedBoxes)
|
||||||
const [volunteerKnowledge, saveVolunteerKnowledge] = useVolunteerKnowledge()
|
const [volunteerKnowledge, saveVolunteerKnowledge] = useVolunteerKnowledge()
|
||||||
const [showUnknownOnly, setShowUnknownOnly] = useState(false)
|
const [showUnknownOnly, setShowUnknownOnly] = useState(false)
|
||||||
@ -53,6 +53,6 @@ const BoxList: React.FC = (): JSX.Element | null => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default memo(BoxList)
|
export default memo(KnowledgeBoxList)
|
||||||
|
|
||||||
export const fetchFor = [fetchBoxListIfNeed, fetchVolunteerKnowledgeSetIfNeed]
|
export const fetchFor = [fetchBoxListIfNeed, fetchVolunteerKnowledgeSetIfNeed]
|
156
src/components/Loan/BoxItem.tsx
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
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)
|
56
src/components/Loan/LoanBoxList.tsx
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import React, { memo, useState } from "react"
|
||||||
|
import { useSelector } from "react-redux"
|
||||||
|
import styles from "./styles.module.scss"
|
||||||
|
import BoxItem from "./BoxItem"
|
||||||
|
import { fetchBoxListIfNeed, selectSortedUniqueDetailedBoxes } from "../../store/boxList"
|
||||||
|
import { fetchVolunteerLoanSetIfNeed, useVolunteerLoan } from "../../store/volunteerLoanSet"
|
||||||
|
|
||||||
|
const LoanBoxList: React.FC = (): JSX.Element | null => {
|
||||||
|
const detailedBoxes = useSelector(selectSortedUniqueDetailedBoxes)
|
||||||
|
const [volunteerLoan, saveVolunteerLoan] = useVolunteerLoan()
|
||||||
|
const [showUnknownOnly, setShowUnknownOnly] = useState(false)
|
||||||
|
|
||||||
|
const onShowUnknownOnly = (e: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
|
setShowUnknownOnly(e.target.checked)
|
||||||
|
|
||||||
|
if (!detailedBoxes || detailedBoxes.length === 0) return null
|
||||||
|
|
||||||
|
const boxesToShow = detailedBoxes.filter(
|
||||||
|
(box) =>
|
||||||
|
!box ||
|
||||||
|
!showUnknownOnly ||
|
||||||
|
!volunteerLoan ||
|
||||||
|
(!volunteerLoan.loanable.includes(box.gameId) &&
|
||||||
|
!volunteerLoan.playable.includes(box.gameId) &&
|
||||||
|
!volunteerLoan.giftable.includes(box.gameId) &&
|
||||||
|
!volunteerLoan.noOpinion.includes(box.gameId))
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.loanThings}>
|
||||||
|
<label className={styles.showUnknownOnlyLabel}>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="showUnknownOnly"
|
||||||
|
onChange={onShowUnknownOnly}
|
||||||
|
checked={showUnknownOnly}
|
||||||
|
/>{" "}
|
||||||
|
Uniquement les non-renseignés
|
||||||
|
</label>
|
||||||
|
<ul className={styles.boxList}>
|
||||||
|
{boxesToShow.map((detailedBox: any) => (
|
||||||
|
<BoxItem
|
||||||
|
detailedBox={detailedBox}
|
||||||
|
volunteerLoan={volunteerLoan}
|
||||||
|
saveVolunteerLoan={saveVolunteerLoan}
|
||||||
|
key={detailedBox.id}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(LoanBoxList)
|
||||||
|
|
||||||
|
export const fetchFor = [fetchBoxListIfNeed, fetchVolunteerLoanSetIfNeed]
|
57
src/components/Loan/LoanIntro.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import classnames from "classnames"
|
||||||
|
import React from "react"
|
||||||
|
import styles from "./styles.module.scss"
|
||||||
|
|
||||||
|
const LoanIntro: React.FC = (): JSX.Element => (
|
||||||
|
<div className={styles.loanThings}>
|
||||||
|
<h1>Emprunt et tri des jeux</h1>
|
||||||
|
<p>
|
||||||
|
Lors du brunch des bénévoles ayant contribué au dernier festival, des boîtes de jeux en
|
||||||
|
trop dans la ludothèque seront données, et des boîtes qui passe l'année dans la cave
|
||||||
|
entre deux éditions seront prêtées pour 4 mois.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Pour chaque jeu, active le bouton :<br />
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={classnames(styles.loanButton, styles.loanable, styles.loanableCheckbox)}
|
||||||
|
>
|
||||||
|
|
||||||
|
</button>{" "}
|
||||||
|
si tu veux l'emprunter lors du brunch ou d'un rdv bénévoles mensuel
|
||||||
|
<br />
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={classnames(styles.loanButton, styles.playable, styles.playableCheckbox)}
|
||||||
|
>
|
||||||
|
|
||||||
|
</button>{" "}
|
||||||
|
si tu penses que les visiteurs du festival (ou même les bénévoles) doivent pouvoir y
|
||||||
|
jouer
|
||||||
|
<br />
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={classnames(styles.loanButton, styles.giftable, styles.giftableCheckbox)}
|
||||||
|
>
|
||||||
|
|
||||||
|
</button>{" "}
|
||||||
|
si tu aimerais le récupérer comme cadeau lors du brunch (ou rdv bénévole si tu ne peux
|
||||||
|
pas venir)
|
||||||
|
<br />
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={classnames(
|
||||||
|
styles.loanButton,
|
||||||
|
styles.noOpinion,
|
||||||
|
styles.noOpinionCheckbox
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
|
||||||
|
</button>{" "}
|
||||||
|
si tu n'as pas d'avis sur ce jeu
|
||||||
|
<br />
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default LoanIntro
|
292
src/components/Loan/styles.module.scss
Executable file
@ -0,0 +1,292 @@
|
|||||||
|
@import "../../theme/variables";
|
||||||
|
@import "../../theme/mixins";
|
||||||
|
|
||||||
|
.showUnknownOnlyLabel {
|
||||||
|
text-align: left;
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
width: 280px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boxList {
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boxItem {
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photoContainer {
|
||||||
|
height: 50px;
|
||||||
|
width: 73px;
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.photo {
|
||||||
|
height: 50px;
|
||||||
|
max-width: 73px;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.a371 {
|
||||||
|
background: url("../../app/img/gameImages/371.png") no-repeat;
|
||||||
|
}
|
||||||
|
.a888 {
|
||||||
|
background: url("../../app/img/gameImages/888.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a918 {
|
||||||
|
background: url("../../app/img/gameImages/918.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a992 {
|
||||||
|
background: url("../../app/img/gameImages/992.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a993 {
|
||||||
|
background: url("../../app/img/gameImages/993.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a994 {
|
||||||
|
background: url("../../app/img/gameImages/994.png") no-repeat;
|
||||||
|
}
|
||||||
|
.a995 {
|
||||||
|
background: url("../../app/img/gameImages/995.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a996 {
|
||||||
|
background: url("../../app/img/gameImages/996.png") no-repeat;
|
||||||
|
}
|
||||||
|
.a997 {
|
||||||
|
background: url("../../app/img/gameImages/997.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a998 {
|
||||||
|
background: url("../../app/img/gameImages/998.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a999 {
|
||||||
|
background: url("../../app/img/gameImages/999.png") no-repeat;
|
||||||
|
}
|
||||||
|
.a1000 {
|
||||||
|
background: url("../../app/img/gameImages/1000.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1001 {
|
||||||
|
background: url("../../app/img/gameImages/1001.png") no-repeat;
|
||||||
|
}
|
||||||
|
.a1002 {
|
||||||
|
background: url("../../app/img/gameImages/1002.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1003 {
|
||||||
|
background: url("../../app/img/gameImages/1003.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1004 {
|
||||||
|
background: url("../../app/img/gameImages/1004.png") no-repeat;
|
||||||
|
}
|
||||||
|
.a1005 {
|
||||||
|
background: url("../../app/img/gameImages/1005.png") no-repeat;
|
||||||
|
}
|
||||||
|
.a1007 {
|
||||||
|
background: url("../../app/img/gameImages/1007.png") no-repeat;
|
||||||
|
}
|
||||||
|
.a1008 {
|
||||||
|
background: url("../../app/img/gameImages/1008.png") no-repeat;
|
||||||
|
}
|
||||||
|
.a1009 {
|
||||||
|
background: url("../../app/img/gameImages/1009.png") no-repeat;
|
||||||
|
}
|
||||||
|
.a1010 {
|
||||||
|
background: url("../../app/img/gameImages/1010.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1011 {
|
||||||
|
background: url("../../app/img/gameImages/1011.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1012 {
|
||||||
|
background: url("../../app/img/gameImages/1012.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1013 {
|
||||||
|
background: url("../../app/img/gameImages/1013.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1014 {
|
||||||
|
background: url("../../app/img/gameImages/1014.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1015 {
|
||||||
|
background: url("../../app/img/gameImages/1015.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1016 {
|
||||||
|
background: url("../../app/img/gameImages/1016.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1017 {
|
||||||
|
background: url("../../app/img/gameImages/1017.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1018 {
|
||||||
|
background: url("../../app/img/gameImages/1018.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1019 {
|
||||||
|
background: url("../../app/img/gameImages/1019.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1020 {
|
||||||
|
background: url("../../app/img/gameImages/1020.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1021 {
|
||||||
|
background: url("../../app/img/gameImages/1021.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1022 {
|
||||||
|
background: url("../../app/img/gameImages/1022.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1023 {
|
||||||
|
background: url("../../app/img/gameImages/1023.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1024 {
|
||||||
|
background: url("../../app/img/gameImages/1024.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1025 {
|
||||||
|
background: url("../../app/img/gameImages/1025.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1026 {
|
||||||
|
background: url("../../app/img/gameImages/1026.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1027 {
|
||||||
|
background: url("../../app/img/gameImages/1027.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1028 {
|
||||||
|
background: url("../../app/img/gameImages/1028.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1029 {
|
||||||
|
background: url("../../app/img/gameImages/1029.jpeg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1030 {
|
||||||
|
background: url("../../app/img/gameImages/1030.jpeg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1031 {
|
||||||
|
background: url("../../app/img/gameImages/1031.jpg") no-repeat;
|
||||||
|
}
|
||||||
|
.a1032 {
|
||||||
|
background: url("../../app/img/gameImages/1032.png") no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alternateBox {
|
||||||
|
vertical-align: middle;
|
||||||
|
display: inline-block;
|
||||||
|
width: inherit;
|
||||||
|
height: inherit;
|
||||||
|
background-size: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.titleContainer {
|
||||||
|
height: 50px;
|
||||||
|
width: 198px;
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.shorterTitle {
|
||||||
|
width: 168px;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
height: 50px;
|
||||||
|
font-weight: bold;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.poufpaf {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
background: url("../../app/img/poufpaf.png");
|
||||||
|
background-size: contain;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noPoufpaf {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loanList {
|
||||||
|
@include clear-ul-style;
|
||||||
|
|
||||||
|
width: 266px;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loanItem {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loanButton {
|
||||||
|
margin: 0;
|
||||||
|
padding: 4px 0 3px;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
width: 60px;
|
||||||
|
text-align: center;
|
||||||
|
color: $color-grey-medium;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: $color-yellow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loanCheckbox {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loanCheckboxImg {
|
||||||
|
vertical-align: middle;
|
||||||
|
display: inline-block;
|
||||||
|
width: 2em;
|
||||||
|
height: 1.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loanThings {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loanThings .loanable {
|
||||||
|
background-color: $color-loanable;
|
||||||
|
&.active {
|
||||||
|
background-color: $color-active-loanable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.loanableCheckbox {
|
||||||
|
background: url("../../app/img/loanable.svg") no-repeat center center;
|
||||||
|
background-size: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loanThings .playable {
|
||||||
|
background-color: $color-playable;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: $color-active-playable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.playableCheckbox {
|
||||||
|
background: url("../../app/img/playable.svg") no-repeat center center;
|
||||||
|
background-size: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loanThings .giftable {
|
||||||
|
background-color: $color-giftable;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: $color-active-giftable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.giftableCheckbox {
|
||||||
|
background: url("../../app/img/giftable.svg") no-repeat center center;
|
||||||
|
background-size: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loanThings .noOpinion {
|
||||||
|
background-color: $color-no-opinion;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: $color-active-no-opinion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.noOpinionCheckbox {
|
||||||
|
background: url("../../app/img/noOpinion.svg") no-repeat center center;
|
||||||
|
background-size: contain;
|
||||||
|
}
|
@ -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="Emprunts" pathname="/emprunts" />
|
||||||
{/* <MenuItem name="Mes connaissances" pathname="/connaissances" /> */}
|
{/* <MenuItem name="Mes connaissances" pathname="/connaissances" /> */}
|
||||||
<RestrictMenuItem
|
<RestrictMenuItem
|
||||||
role={ROLES.ASSIGNER}
|
role={ROLES.ASSIGNER}
|
||||||
|
@ -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 LoanIntro from "./Loan/LoanIntro"
|
||||||
import LoginForm from "./LoginForm"
|
import LoginForm from "./LoginForm"
|
||||||
import BoxList, { fetchFor as fetchForKnowledge } from "./Knowledge/BoxList"
|
import KnowledgeBoxList, { fetchFor as fetchForKnowledge } from "./Knowledge/KnowledgeBoxList"
|
||||||
import KnowledgeCard, { fetchFor as fetchForKnowledgeCard } from "./Knowledge/KnowledgeCard"
|
import KnowledgeCard, { fetchFor as fetchForKnowledgeCard } from "./Knowledge/KnowledgeCard"
|
||||||
import KnowledgeIntro from "./Knowledge/KnowledgeIntro"
|
import KnowledgeIntro from "./Knowledge/KnowledgeIntro"
|
||||||
import Asks, { fetchFor as fetchForAsks } from "./Asks"
|
import Asks, { fetchFor as fetchForAsks } from "./Asks"
|
||||||
@ -33,16 +35,19 @@ export {
|
|||||||
fetchForGameDetailsUpdate,
|
fetchForGameDetailsUpdate,
|
||||||
Board,
|
Board,
|
||||||
fetchForBoard,
|
fetchForBoard,
|
||||||
BoxList,
|
|
||||||
fetchForKnowledge,
|
|
||||||
KnowledgeCard,
|
KnowledgeCard,
|
||||||
fetchForKnowledgeCard,
|
fetchForKnowledgeCard,
|
||||||
DayWishesForm,
|
DayWishesForm,
|
||||||
fetchForDayWishesForm,
|
fetchForDayWishesForm,
|
||||||
ErrorBoundary,
|
ErrorBoundary,
|
||||||
GameList,
|
GameList,
|
||||||
|
KnowledgeBoxList,
|
||||||
|
fetchForKnowledge,
|
||||||
KnowledgeIntro,
|
KnowledgeIntro,
|
||||||
Loading,
|
Loading,
|
||||||
|
LoanBoxList,
|
||||||
|
fetchForLoan,
|
||||||
|
LoanIntro,
|
||||||
LoginForm,
|
LoginForm,
|
||||||
Asks,
|
Asks,
|
||||||
fetchForAsks,
|
fetchForAsks,
|
||||||
|
@ -5,7 +5,7 @@ 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 { BoxList, KnowledgeIntro, fetchForKnowledge } from "../../components"
|
import { KnowledgeBoxList, KnowledgeIntro, fetchForKnowledge } from "../../components"
|
||||||
import { selectUserJwtToken } from "../../store/auth"
|
import { selectUserJwtToken } from "../../store/auth"
|
||||||
|
|
||||||
export type Props = RouteComponentProps
|
export type Props = RouteComponentProps
|
||||||
@ -21,7 +21,7 @@ const KnowledgesPage: FC<Props> = (): JSX.Element => {
|
|||||||
<div className={styles.knowledgesContent}>
|
<div className={styles.knowledgesContent}>
|
||||||
<Helmet title="KnowledgesPage" />
|
<Helmet title="KnowledgesPage" />
|
||||||
<KnowledgeIntro />
|
<KnowledgeIntro />
|
||||||
<BoxList />
|
<KnowledgeBoxList />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
33
src/pages/Loan/LoanPage.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 { LoanBoxList, LoanIntro, fetchForLoan } from "../../components"
|
||||||
|
import { selectUserJwtToken } from "../../store/auth"
|
||||||
|
|
||||||
|
export type Props = RouteComponentProps
|
||||||
|
|
||||||
|
const LoanPage: 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.loanPage}>
|
||||||
|
<div className={styles.loanContent}>
|
||||||
|
<Helmet title="LoanPage" />
|
||||||
|
<LoanIntro />
|
||||||
|
<LoanBoxList />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch server-side data here
|
||||||
|
export const loadData = (): AppThunk[] => [...fetchForLoan.map((f) => f())]
|
||||||
|
|
||||||
|
export default memo(LoanPage)
|
16
src/pages/Loan/index.tsx
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
import loadable from "@loadable/component"
|
||||||
|
|
||||||
|
import { Loading, ErrorBoundary } from "../../components"
|
||||||
|
import { Props, loadData } from "./LoanPage"
|
||||||
|
|
||||||
|
const Loan = loadable(() => import("./LoanPage"), {
|
||||||
|
fallback: <Loading />,
|
||||||
|
})
|
||||||
|
|
||||||
|
export default (props: Props): JSX.Element => (
|
||||||
|
<ErrorBoundary>
|
||||||
|
<Loan {...props} />
|
||||||
|
</ErrorBoundary>
|
||||||
|
)
|
||||||
|
|
||||||
|
export { loadData }
|
9
src/pages/Loan/styles.module.scss
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
@import "../../theme/mixins";
|
||||||
|
|
||||||
|
.loanPage {
|
||||||
|
@include page-wrapper-center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loanContent {
|
||||||
|
@include page-content-wrapper(700px);
|
||||||
|
}
|
@ -10,6 +10,7 @@ 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 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"
|
||||||
@ -44,6 +45,11 @@ export default [
|
|||||||
component: AsyncKnowledge,
|
component: AsyncKnowledge,
|
||||||
loadData: loadKnowledgeData,
|
loadData: loadKnowledgeData,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/emprunts",
|
||||||
|
component: AsyncLoan,
|
||||||
|
loadData: loadLoanData,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/fiches",
|
path: "/fiches",
|
||||||
component: AsyncKnowledgeCards,
|
component: AsyncKnowledgeCards,
|
||||||
|
@ -24,9 +24,10 @@ export const detailedBoxListGet = expressAccessor.get(async (list) => {
|
|||||||
id: box?.id || 10000 + game.id,
|
id: box?.id || 10000 + game.id,
|
||||||
gameId: game.id,
|
gameId: game.id,
|
||||||
title: game.title,
|
title: game.title,
|
||||||
|
bggId: game.bggId,
|
||||||
|
bggIdAlternative: game.bggIdAlternative,
|
||||||
bggPhoto: game.bggPhoto,
|
bggPhoto: game.bggPhoto,
|
||||||
poufpaf: game.poufpaf,
|
poufpaf: game.poufpaf,
|
||||||
bggId: game.bggId,
|
|
||||||
container: box?.container || "Non stocké",
|
container: box?.container || "Non stocké",
|
||||||
playersMin: game.playersMin,
|
playersMin: game.playersMin,
|
||||||
playersMax: game.playersMax,
|
playersMax: game.playersMax,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import axios from "axios"
|
import axios from "axios"
|
||||||
import { Parser } from "xml2js"
|
import { Parser } from "xml2js"
|
||||||
import { assign, cloneDeep, find, maxBy, some } from "lodash"
|
import { assign, cloneDeep, find } from "lodash"
|
||||||
import ExpressAccessors from "./expressAccessors"
|
import ExpressAccessors from "./expressAccessors"
|
||||||
import { Game, GameWithoutId, translationGame } from "../../services/games"
|
import { Game, GameWithoutId, translationGame } from "../../services/games"
|
||||||
|
|
||||||
@ -26,28 +26,41 @@ export const gameDetailsUpdate = expressAccessor.listSet(
|
|||||||
|
|
||||||
newList.forEach((game, index, arr) => {
|
newList.forEach((game, index, arr) => {
|
||||||
const box = find(parsed.items.item, (item: any) => game.bggId === +item.$.objectid)
|
const box = find(parsed.items.item, (item: any) => game.bggId === +item.$.objectid)
|
||||||
if (box && game.bggPhoto === "") {
|
if (box) {
|
||||||
assign(arr[index], xmlToGame(game.id, box), {
|
if (game.bggPhoto === "" || game.duration === 0) {
|
||||||
ean: arr[index].ean,
|
assign(arr[index], xmlToGame(game.id, box), {
|
||||||
type: arr[index].type,
|
title: arr[index].title,
|
||||||
|
ean: arr[index].ean || 0,
|
||||||
|
type: arr[index].type,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assign(arr[index], {
|
||||||
|
bggId: 0,
|
||||||
|
playersMin: 0,
|
||||||
|
playersMax: 0,
|
||||||
|
duration: 0,
|
||||||
|
ean: 0,
|
||||||
|
toBeKnown: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const newGames = parsed.items.item.filter(
|
// // Add to DB game that were only present on BGG
|
||||||
(item: any) =>
|
// const newGames = parsed.items.item.filter(
|
||||||
(item.status[0]?.$?.own === "1" ||
|
// (item: any) =>
|
||||||
item.status[0]?.$?.want === "1" ||
|
// (item.status[0]?.$?.own === "1" ||
|
||||||
item.status[0]?.$?.wishlist === "1" ||
|
// item.status[0]?.$?.want === "1" ||
|
||||||
item.status[0]?.$?.preordered === "1") &&
|
// item.status[0]?.$?.wishlist === "1" ||
|
||||||
!some(list, (i) => +i.bggId === +item.$.objectid)
|
// item.status[0]?.$?.preordered === "1") &&
|
||||||
)
|
// !some(list, (i) => +i.bggId === +item.$.objectid)
|
||||||
|
// )
|
||||||
|
|
||||||
let id = maxBy(newList, "id")?.id || 0
|
// let id = maxBy(newList, "id")?.id || 0
|
||||||
newGames.forEach((item: any) => {
|
// newGames.forEach((item: any) => {
|
||||||
id += 1
|
// id += 1
|
||||||
newList.push(xmlToGame(id, item))
|
// newList.push(xmlToGame(id, item))
|
||||||
})
|
// })
|
||||||
|
|
||||||
return {
|
return {
|
||||||
toDatabase: newList,
|
toDatabase: newList,
|
||||||
@ -59,13 +72,15 @@ export const gameDetailsUpdate = expressAccessor.listSet(
|
|||||||
function xmlToGame(id: number, item: any): Game {
|
function xmlToGame(id: number, item: any): Game {
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
title: item.name?.[0]._ || "",
|
title: "",
|
||||||
|
bggId: +item.$.objectid || 0,
|
||||||
|
bggIdAlternative: "",
|
||||||
|
bggTitle: item.name?.[0]._ || "",
|
||||||
playersMin: item.stats?.[0]?.$?.minplayers || 0,
|
playersMin: item.stats?.[0]?.$?.minplayers || 0,
|
||||||
playersMax: item.stats?.[0]?.$?.maxplayers || 0,
|
playersMax: item.stats?.[0]?.$?.maxplayers || 0,
|
||||||
duration: item.stats?.[0]?.$?.playingtime || 0,
|
duration: item.stats?.[0]?.$?.playingtime || 0,
|
||||||
type: "Famille",
|
type: "Famille",
|
||||||
poufpaf: "",
|
poufpaf: "",
|
||||||
bggId: +item.$.objectid || 0,
|
|
||||||
ean: "",
|
ean: "",
|
||||||
bggPhoto: item.thumbnail?.[0] || "",
|
bggPhoto: item.thumbnail?.[0] || "",
|
||||||
toBeKnown: true,
|
toBeKnown: true,
|
||||||
|
@ -20,6 +20,7 @@ import {
|
|||||||
VolunteerKnowledge,
|
VolunteerKnowledge,
|
||||||
VolunteerDetailedKnowledge,
|
VolunteerDetailedKnowledge,
|
||||||
VolunteerPersonalInfo,
|
VolunteerPersonalInfo,
|
||||||
|
VolunteerLoan,
|
||||||
} 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"
|
||||||
@ -552,6 +553,31 @@ export const volunteerDetailedKnowledgeList = expressAccessor.get(async (list) =
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const volunteerLoanSet = expressAccessor.set(async (list, body, id) => {
|
||||||
|
const requestedId = +body[0] || id
|
||||||
|
const volunteer: Volunteer | undefined = list.find((v) => v.id === requestedId)
|
||||||
|
if (!volunteer) {
|
||||||
|
throw Error(`Il n'y a aucun bénévole avec cet identifiant ${requestedId}`)
|
||||||
|
}
|
||||||
|
const loan = body[1] as VolunteerLoan
|
||||||
|
const newVolunteer: Volunteer = cloneDeep(volunteer)
|
||||||
|
if (loan?.loanable !== undefined) newVolunteer.loanable = loan.loanable
|
||||||
|
if (loan?.playable !== undefined) newVolunteer.playable = loan.playable
|
||||||
|
if (loan?.giftable !== undefined) newVolunteer.giftable = loan.giftable
|
||||||
|
if (loan?.noOpinion !== undefined) newVolunteer.noOpinion = loan.noOpinion
|
||||||
|
|
||||||
|
return {
|
||||||
|
toDatabase: newVolunteer,
|
||||||
|
toCaller: {
|
||||||
|
id: newVolunteer.id,
|
||||||
|
loanable: newVolunteer.loanable,
|
||||||
|
playable: newVolunteer.playable,
|
||||||
|
giftable: newVolunteer.giftable,
|
||||||
|
noOpinion: newVolunteer.noOpinion,
|
||||||
|
} as VolunteerLoan,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
function getUniqueNickname(list: Volunteer[], volunteer: Volunteer): string {
|
function getUniqueNickname(list: Volunteer[], volunteer: Volunteer): string {
|
||||||
const lastnameList = list
|
const lastnameList = list
|
||||||
.filter((v) => v.firstname === volunteer.firstname)
|
.filter((v) => v.firstname === volunteer.firstname)
|
||||||
|
@ -39,6 +39,7 @@ import {
|
|||||||
volunteerKnowledgeSet,
|
volunteerKnowledgeSet,
|
||||||
volunteerAddNew,
|
volunteerAddNew,
|
||||||
volunteerDetailedKnowledgeList,
|
volunteerDetailedKnowledgeList,
|
||||||
|
volunteerLoanSet,
|
||||||
} from "./gsheets/volunteers"
|
} from "./gsheets/volunteers"
|
||||||
import { wishListGet, wishAdd } from "./gsheets/wishes"
|
import { wishListGet, wishAdd } from "./gsheets/wishes"
|
||||||
import config from "../config"
|
import config from "../config"
|
||||||
@ -119,6 +120,7 @@ app.post(
|
|||||||
secure as RequestHandler,
|
secure as RequestHandler,
|
||||||
volunteerDetailedKnowledgeList
|
volunteerDetailedKnowledgeList
|
||||||
)
|
)
|
||||||
|
app.post("/VolunteerLoanSet", secure as RequestHandler, volunteerLoanSet)
|
||||||
app.post(
|
app.post(
|
||||||
"/VolunteerParticipationDetailsSet",
|
"/VolunteerParticipationDetailsSet",
|
||||||
secure as RequestHandler,
|
secure as RequestHandler,
|
||||||
|
@ -38,12 +38,14 @@ export class DetailedBox {
|
|||||||
|
|
||||||
title = new Game().title
|
title = new Game().title
|
||||||
|
|
||||||
|
bggId = new Game().bggId
|
||||||
|
|
||||||
|
bggIdAlternative = new Game().bggIdAlternative
|
||||||
|
|
||||||
bggPhoto = new Game().bggPhoto
|
bggPhoto = new Game().bggPhoto
|
||||||
|
|
||||||
poufpaf = new Game().poufpaf
|
poufpaf = new Game().poufpaf
|
||||||
|
|
||||||
bggId = new Game().bggId
|
|
||||||
|
|
||||||
playersMin = new Game().playersMin
|
playersMin = new Game().playersMin
|
||||||
|
|
||||||
playersMax = new Game().playersMax
|
playersMax = new Game().playersMax
|
||||||
|
@ -3,6 +3,12 @@ export class Game {
|
|||||||
|
|
||||||
title = ""
|
title = ""
|
||||||
|
|
||||||
|
bggId = 0
|
||||||
|
|
||||||
|
bggIdAlternative = ""
|
||||||
|
|
||||||
|
bggTitle = ""
|
||||||
|
|
||||||
playersMin = 0
|
playersMin = 0
|
||||||
|
|
||||||
playersMax = 0
|
playersMax = 0
|
||||||
@ -13,8 +19,6 @@ export class Game {
|
|||||||
|
|
||||||
poufpaf = ""
|
poufpaf = ""
|
||||||
|
|
||||||
bggId = 0
|
|
||||||
|
|
||||||
ean = ""
|
ean = ""
|
||||||
|
|
||||||
bggPhoto = ""
|
bggPhoto = ""
|
||||||
@ -25,12 +29,14 @@ export class Game {
|
|||||||
export const translationGame: { [k in keyof Game]: string } = {
|
export const translationGame: { [k in keyof Game]: string } = {
|
||||||
id: "id",
|
id: "id",
|
||||||
title: "titre",
|
title: "titre",
|
||||||
|
bggId: "bggId",
|
||||||
|
bggIdAlternative: "bggIdAlternative",
|
||||||
|
bggTitle: "titreBgg",
|
||||||
playersMin: "minJoueurs",
|
playersMin: "minJoueurs",
|
||||||
playersMax: "maxJoueurs",
|
playersMax: "maxJoueurs",
|
||||||
duration: "duree",
|
duration: "duree",
|
||||||
type: "type",
|
type: "type",
|
||||||
poufpaf: "poufpaf",
|
poufpaf: "poufpaf",
|
||||||
bggId: "bggId",
|
|
||||||
ean: "ean",
|
ean: "ean",
|
||||||
bggPhoto: "bggPhoto",
|
bggPhoto: "bggPhoto",
|
||||||
toBeKnown: "àConnaitre",
|
toBeKnown: "àConnaitre",
|
||||||
|
@ -58,6 +58,14 @@ export class Volunteer implements VolunteerPartial {
|
|||||||
|
|
||||||
niet: number[] = []
|
niet: number[] = []
|
||||||
|
|
||||||
|
loanable: number[] = []
|
||||||
|
|
||||||
|
playable: number[] = []
|
||||||
|
|
||||||
|
giftable: number[] = []
|
||||||
|
|
||||||
|
noOpinion: number[] = []
|
||||||
|
|
||||||
needsHosting = false
|
needsHosting = false
|
||||||
|
|
||||||
canHostCount = 0
|
canHostCount = 0
|
||||||
@ -99,6 +107,10 @@ export const translationVolunteer: { [k in keyof Volunteer]: string } = {
|
|||||||
ok: "OK",
|
ok: "OK",
|
||||||
bof: "Bof",
|
bof: "Bof",
|
||||||
niet: "Niet",
|
niet: "Niet",
|
||||||
|
loanable: "empruntable",
|
||||||
|
playable: "jouable",
|
||||||
|
giftable: "offrable",
|
||||||
|
noOpinion: "sansAvis",
|
||||||
needsHosting: "besoinHébergement",
|
needsHosting: "besoinHébergement",
|
||||||
canHostCount: "nombreHébergés",
|
canHostCount: "nombreHébergés",
|
||||||
distanceToFestival: "distanceAuFestival",
|
distanceToFestival: "distanceAuFestival",
|
||||||
@ -148,6 +160,10 @@ export const volunteerExample: Volunteer = {
|
|||||||
ok: [5, 7, 24, 26, 31, 38, 50, 52, 54, 58],
|
ok: [5, 7, 24, 26, 31, 38, 50, 52, 54, 58],
|
||||||
bof: [9, 12, 16, 27, 34, 35, 36],
|
bof: [9, 12, 16, 27, 34, 35, 36],
|
||||||
niet: [13, 18, 19, 23, 47, 53, 59, 67],
|
niet: [13, 18, 19, 23, 47, 53, 59, 67],
|
||||||
|
loanable: [5, 7],
|
||||||
|
playable: [34, 35, 36],
|
||||||
|
giftable: [13, 67],
|
||||||
|
noOpinion: [3, 4],
|
||||||
needsHosting: false,
|
needsHosting: false,
|
||||||
canHostCount: 0,
|
canHostCount: 0,
|
||||||
distanceToFestival: 0,
|
distanceToFestival: 0,
|
||||||
@ -247,3 +263,12 @@ export interface VolunteerDetailedKnowledge {
|
|||||||
niet: Volunteer["niet"]
|
niet: Volunteer["niet"]
|
||||||
dayWishes: Volunteer["dayWishes"]
|
dayWishes: Volunteer["dayWishes"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type VolunteerLoanWithoutId = Omit<VolunteerLoan, "id">
|
||||||
|
export interface VolunteerLoan {
|
||||||
|
id: Volunteer["id"]
|
||||||
|
loanable: Volunteer["loanable"]
|
||||||
|
playable: Volunteer["playable"]
|
||||||
|
giftable: Volunteer["giftable"]
|
||||||
|
noOpinion: Volunteer["noOpinion"]
|
||||||
|
}
|
||||||
|
@ -13,6 +13,7 @@ import {
|
|||||||
VolunteerKnowledge,
|
VolunteerKnowledge,
|
||||||
VolunteerMeals,
|
VolunteerMeals,
|
||||||
VolunteerPersonalInfo,
|
VolunteerPersonalInfo,
|
||||||
|
VolunteerLoan,
|
||||||
} from "./volunteers"
|
} from "./volunteers"
|
||||||
|
|
||||||
const serviceAccessors = new ServiceAccessors<VolunteerWithoutId, Volunteer>(elementName)
|
const serviceAccessors = new ServiceAccessors<VolunteerWithoutId, Volunteer>(elementName)
|
||||||
@ -64,3 +65,6 @@ export const volunteerKnowledgeSet =
|
|||||||
export const volunteerDetailedKnowledgeList = serviceAccessors.securedCustomPost<[number]>(
|
export const volunteerDetailedKnowledgeList = serviceAccessors.securedCustomPost<[number]>(
|
||||||
"DetailedKnowledgeListGet"
|
"DetailedKnowledgeListGet"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
export const volunteerLoanSet =
|
||||||
|
serviceAccessors.securedCustomPost<[number, Partial<VolunteerLoan>]>("LoanSet")
|
||||||
|
@ -17,6 +17,9 @@ const mockData: Game[] = [
|
|||||||
{
|
{
|
||||||
id: 5,
|
id: 5,
|
||||||
title: "6 qui prend!",
|
title: "6 qui prend!",
|
||||||
|
bggId: 432,
|
||||||
|
bggIdAlternative: "",
|
||||||
|
bggTitle: "6 nimmt!",
|
||||||
playersMin: 2,
|
playersMin: 2,
|
||||||
playersMax: 10,
|
playersMax: 10,
|
||||||
duration: 45,
|
duration: 45,
|
||||||
@ -24,7 +27,6 @@ const mockData: Game[] = [
|
|||||||
poufpaf: "0-9-2/6-qui-prend-6-nimmt",
|
poufpaf: "0-9-2/6-qui-prend-6-nimmt",
|
||||||
bggPhoto:
|
bggPhoto:
|
||||||
"https://cf.geekdo-images.com/thumb/img/lzczxR5cw7an7tRWeHdOrRtLyes=/fit-in/200x150/pic772547.jpg",
|
"https://cf.geekdo-images.com/thumb/img/lzczxR5cw7an7tRWeHdOrRtLyes=/fit-in/200x150/pic772547.jpg",
|
||||||
bggId: 432,
|
|
||||||
ean: "3421272101313",
|
ean: "3421272101313",
|
||||||
toBeKnown: false,
|
toBeKnown: false,
|
||||||
},
|
},
|
||||||
|
@ -20,6 +20,7 @@ import volunteerForgot from "./volunteerForgot"
|
|||||||
import volunteerHostingSet from "./volunteerHostingSet"
|
import volunteerHostingSet from "./volunteerHostingSet"
|
||||||
import volunteerMealsSet from "./volunteerMealsSet"
|
import volunteerMealsSet from "./volunteerMealsSet"
|
||||||
import volunteerList from "./volunteerList"
|
import volunteerList from "./volunteerList"
|
||||||
|
import volunteerLoanSet from "./volunteerLoanSet"
|
||||||
import volunteerLogin from "./volunteerLogin"
|
import volunteerLogin from "./volunteerLogin"
|
||||||
import volunteerKnowledgeSet from "./volunteerKnowledgeSet"
|
import volunteerKnowledgeSet from "./volunteerKnowledgeSet"
|
||||||
import volunteerDetailedKnowledgeList from "./volunteerDetailedKnowledgeList"
|
import volunteerDetailedKnowledgeList from "./volunteerDetailedKnowledgeList"
|
||||||
@ -53,6 +54,7 @@ export default (history: History) => ({
|
|||||||
volunteerHostingSet,
|
volunteerHostingSet,
|
||||||
volunteerMealsSet,
|
volunteerMealsSet,
|
||||||
volunteerList,
|
volunteerList,
|
||||||
|
volunteerLoanSet,
|
||||||
volunteerLogin,
|
volunteerLogin,
|
||||||
volunteerKnowledgeSet,
|
volunteerKnowledgeSet,
|
||||||
volunteerDetailedKnowledgeList,
|
volunteerDetailedKnowledgeList,
|
||||||
|
85
src/store/volunteerLoanSet.ts
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import { PayloadAction, createSlice } from "@reduxjs/toolkit"
|
||||||
|
import { shallowEqual, useSelector } from "react-redux"
|
||||||
|
import { useCallback } from "react"
|
||||||
|
import { StateRequest, toastError, elementFetch } from "./utils"
|
||||||
|
import { VolunteerLoan } from "../services/volunteers"
|
||||||
|
import { AppThunk, AppState } from "."
|
||||||
|
import { volunteerLoanSet } from "../services/volunteersAccessors"
|
||||||
|
import useAction from "../utils/useAction"
|
||||||
|
import { selectUserJwtToken } from "./auth"
|
||||||
|
|
||||||
|
type StateVolunteerLoanSet = {
|
||||||
|
entity?: VolunteerLoan
|
||||||
|
} & StateRequest
|
||||||
|
|
||||||
|
export const initialState: StateVolunteerLoanSet = {
|
||||||
|
readyStatus: "idle",
|
||||||
|
}
|
||||||
|
|
||||||
|
const volunteerLoanSetSlice = createSlice({
|
||||||
|
name: "volunteerLoanSet",
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
getRequesting: (_) => ({
|
||||||
|
readyStatus: "request",
|
||||||
|
}),
|
||||||
|
getSuccess: (_, { payload }: PayloadAction<VolunteerLoan>) => ({
|
||||||
|
readyStatus: "success",
|
||||||
|
entity: payload,
|
||||||
|
}),
|
||||||
|
getFailure: (_, { payload }: PayloadAction<string>) => ({
|
||||||
|
readyStatus: "failure",
|
||||||
|
error: payload,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default volunteerLoanSetSlice.reducer
|
||||||
|
export const { getRequesting, getSuccess, getFailure } = volunteerLoanSetSlice.actions
|
||||||
|
|
||||||
|
export const fetchVolunteerLoanSet = elementFetch(
|
||||||
|
volunteerLoanSet,
|
||||||
|
getRequesting,
|
||||||
|
getSuccess,
|
||||||
|
getFailure,
|
||||||
|
(error: Error) => toastError(`Erreur lors du chargement des emprunts: ${error.message}`)
|
||||||
|
)
|
||||||
|
|
||||||
|
const shouldFetchVolunteerLoanSet = (state: AppState, id: number) =>
|
||||||
|
state.volunteerLoanSet?.readyStatus !== "success" ||
|
||||||
|
(state.volunteerLoanSet?.entity && state.volunteerLoanSet?.entity?.id !== id)
|
||||||
|
|
||||||
|
export const fetchVolunteerLoanSetIfNeed =
|
||||||
|
(id = 0, loan: Partial<VolunteerLoan> = {}): AppThunk =>
|
||||||
|
(dispatch, getState) => {
|
||||||
|
let jwt = ""
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
;({ jwt, id } = getState().auth)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldFetchVolunteerLoanSet(getState(), id))
|
||||||
|
return dispatch(fetchVolunteerLoanSet(jwt, id, loan))
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetFunction = (newVolunteerLoan: VolunteerLoan) => void
|
||||||
|
|
||||||
|
export const useVolunteerLoan = (): [VolunteerLoan | undefined, SetFunction] => {
|
||||||
|
const save = useAction(fetchVolunteerLoanSet)
|
||||||
|
const jwtToken = useSelector(selectUserJwtToken)
|
||||||
|
const volunteerLoan = useSelector(
|
||||||
|
(state: AppState) => state.volunteerLoanSet?.entity,
|
||||||
|
shallowEqual
|
||||||
|
)
|
||||||
|
|
||||||
|
const saveVolunteerLoan: SetFunction = useCallback(
|
||||||
|
(newVolunteerLoan) => {
|
||||||
|
save(jwtToken, 0, newVolunteerLoan)
|
||||||
|
},
|
||||||
|
[save, jwtToken]
|
||||||
|
)
|
||||||
|
|
||||||
|
return [volunteerLoan, saveVolunteerLoan]
|
||||||
|
}
|
@ -19,6 +19,15 @@ $color-active-bof: rgb(180, 124, 19);
|
|||||||
$color-niet: rgb(233, 215, 217);
|
$color-niet: rgb(233, 215, 217);
|
||||||
$color-active-niet: rgb(158, 17, 41);
|
$color-active-niet: rgb(158, 17, 41);
|
||||||
|
|
||||||
|
$color-no-opinion: rgb(232, 223, 235);
|
||||||
|
$color-active-no-opinion: rgb(170, 87, 196);
|
||||||
|
$color-loanable: rgb(215, 233, 217);
|
||||||
|
$color-active-loanable: rgb(50, 107, 38);
|
||||||
|
$color-playable: rgb(231, 227, 214);
|
||||||
|
$color-active-playable: rgb(175, 139, 73);
|
||||||
|
$color-giftable: rgb(233, 215, 217);
|
||||||
|
$color-active-giftable: rgb(160, 70, 85);
|
||||||
|
|
||||||
$border-large: 4px solid $color-black;
|
$border-large: 4px solid $color-black;
|
||||||
$border-thin: 2px solid $color-black;
|
$border-thin: 2px solid $color-black;
|
||||||
|
|
||||||
|