add component to display day wishes

This commit is contained in:
memeriau 2022-01-29 16:49:34 +01:00
parent bf649aa040
commit 2bfd8d2a7b
12 changed files with 269 additions and 113 deletions

View File

@ -1,76 +1,30 @@
import { FC, memo, useCallback, useEffect, useRef, useState } from "react" import { FC, memo } from "react"
import classnames from "classnames"
import get from "lodash/get" import get from "lodash/get"
import set from "lodash/set"
import styles from "./styles.module.scss" import styles from "./styles.module.scss"
import { import { getDayLabel, useUserDayWishes } from "../days.utils"
daysChoice,
daysChoiceSelectionDefaultState,
selectionChoices,
useUserDayWishes,
} from "./days.utils"
const DayWishes: FC = (): JSX.Element | null => { const DayWishes: FC = (): JSX.Element | null => {
const [selection, setSelection] = useState(daysChoiceSelectionDefaultState) const [userWishes] = useUserDayWishes()
const commentRef = useRef<HTMLTextAreaElement | null>(null) const dayWishesString = get(userWishes, "dayWishes", []).map(getDayLabel).join(", ")
const [userWishes, saveWishes] = useUserDayWishes() const comment = get(userWishes, "dayWishesComment", "")
useEffect(() => {
if (!userWishes) return
console.log("userWishes", userWishes)
const newSelection = get(userWishes, "dayWishes", []).reduce(
(acc: selectionChoices, day: string) => ({
...acc,
[day]: true,
}),
daysChoice
)
setSelection(newSelection)
set(commentRef, "current.value", get(userWishes, "dayWishesComment", ""))
}, [setSelection, commentRef, userWishes])
const onChoiceClick = useCallback(
(id) => {
setSelection({
...selection,
[id]: !selection[id],
})
},
[selection, setSelection]
)
const onChoiceSubmit = useCallback(() => {
const comment = get(commentRef, "current.value", "")
const days = daysChoice.map(({ id }) => id).filter((id) => selection[id])
saveWishes(days, comment)
}, [selection, commentRef, saveWishes])
return ( return (
<div className={styles.dayWishes}> <div className={styles.dayWishes}>
<div className={styles.dayWishesTitle}>Jours de présence</div> <div className={styles.daysLine}>
<ul className={styles.dayWishesList}> <span className={styles.dayLineTitle}>Mes jours de présence :</span>
{daysChoice.map(({ id, label }) => ( {dayWishesString && <span>{dayWishesString}</span>}
<li key={id} className={styles.dayWishesItem}> {!dayWishesString && <span>Non renseignés</span>}
<button
type="button"
onClick={() => onChoiceClick(id)}
className={classnames(
styles.dayWishesButton,
selection[id] && styles.active
)}
>
{label}
</button>
</li>
))}
</ul>
<div className={styles.dayWishCommentWrapper}>
<label htmlFor="day-choice-comment">Commentaires</label>
<textarea id="day-choice-comment" ref={commentRef} />
</div> </div>
<div className={styles.dayWishesButtonWrapper}> {comment && (
<button type="submit" onClick={onChoiceSubmit}> <div className={styles.commentLine}>
Enregistrer <span className={styles.commentLineTitle}>Mon commentaire :</span>
</button> <span className={styles.commentLineText}>
{get(userWishes, "dayWishesComment", "")}
</span>
</div>
)}
<div className={styles.editButton}>
<button type="button">Modifier</button>
</div> </div>
</div> </div>
) )

View File

@ -2,59 +2,39 @@
@import "../../../theme/mixins"; @import "../../../theme/mixins";
.dayWishes { .dayWishes {
width: 470px; @include inner-content-wrapper();
position: relative;
} }
.dayWishesTitle { .daysLine,
padding: 4px; .commentLine {
font-weight: bold; span {
text-align: center;
}
.dayWishesList {
@include clear-ul-style;
}
.dayWishesItem {
display: inline-block; display: inline-block;
margin: 2px;
}
.dayWishesButton {
margin: 0;
padding: 5px 0 4px;
border: 0;
border-radius: 0;
width: 90px;
text-align: center;
color: $color-grey-dark;
background-color: $color-grey-light;
cursor: pointer;
&.active {
color: $color-yellow;
background-color: $color-black;
} }
} }
.dayWishCommentWrapper { .dayLineTitle {
margin: 6px 0; padding-right: 5px;
font-weight: bold;
label {
display: block;
padding-left: 4px;
}
textarea {
width: 100%;
height: 50px;
padding: 5px;
border: 1px solid $color-grey-light;
background-color: $color-grey-lighter;
outline: 0;
}
} }
.dayWishesButtonWrapper { .dayLineEmpty {
margin-bottom: 10px; color: $color-red;
text-align: center; font-style: italic;
}
.commentLineTitle {
padding-right: 5px;
}
.commentLineText {
font-style: italic;
}
.editButton {
@include vertical-center();
position: absolute;
right: 20px;
} }

View File

@ -0,0 +1,78 @@
import { FC, memo, useCallback, useEffect, useRef, useState } from "react"
import classnames from "classnames"
import get from "lodash/get"
import set from "lodash/set"
import styles from "./styles.module.scss"
import {
daysChoice,
daysChoiceSelectionDefaultState,
selectionChoices,
useUserDayWishes,
} from "../days.utils"
const DayWishesForm: FC = (): JSX.Element | null => {
const [selection, setSelection] = useState(daysChoiceSelectionDefaultState)
const commentRef = useRef<HTMLTextAreaElement | null>(null)
const [userWishes, saveWishes] = useUserDayWishes()
useEffect(() => {
if (!userWishes) return
const newSelection = get(userWishes, "dayWishes", []).reduce(
(acc: selectionChoices, day: string) => ({
...acc,
[day]: true,
}),
daysChoice
)
setSelection(newSelection)
set(commentRef, "current.value", get(userWishes, "dayWishesComment", ""))
}, [setSelection, commentRef, userWishes])
const onChoiceClick = useCallback(
(id) => {
setSelection({
...selection,
[id]: !selection[id],
})
},
[selection, setSelection]
)
const onChoiceSubmit = useCallback(() => {
const comment = get(commentRef, "current.value", "")
const days = daysChoice.map(({ id }) => id).filter((id) => selection[id])
saveWishes(days, comment)
}, [selection, commentRef, saveWishes])
return (
<div className={styles.dayWishesForm}>
<div className={styles.dayWishesTitle}>Mes jours de présence</div>
<ul className={styles.dayWishesList}>
{daysChoice.map(({ id, label }) => (
<li key={id} className={styles.dayWishesItem}>
<button
type="button"
onClick={() => onChoiceClick(id)}
className={classnames(
styles.dayWishesButton,
selection[id] && styles.active
)}
>
{label}
</button>
</li>
))}
</ul>
<div className={styles.dayWishCommentWrapper}>
<label htmlFor="day-choice-comment">Un commentaire, une précision ?</label>
<textarea id="day-choice-comment" ref={commentRef} />
</div>
<div className={styles.dayWishesButtonWrapper}>
<button type="submit" onClick={onChoiceSubmit}>
Enregistrer
</button>
</div>
</div>
)
}
export default memo(DayWishesForm)

View File

@ -0,0 +1,60 @@
@import "../../../theme/variables";
@import "../../../theme/mixins";
.dayWishesForm {
width: 470px;
}
.dayWishesTitle {
padding: 4px;
font-weight: bold;
text-align: center;
}
.dayWishesList {
@include clear-ul-style;
}
.dayWishesItem {
display: inline-block;
margin: 2px;
}
.dayWishesButton {
margin: 0;
padding: 5px 0 4px;
border: 0;
border-radius: 0;
width: 90px;
text-align: center;
color: $color-grey-dark;
background-color: $color-grey-light;
cursor: pointer;
&.active {
color: $color-yellow;
background-color: $color-black;
}
}
.dayWishCommentWrapper {
margin: 6px 0;
label {
display: block;
padding: 6px 0 2px 4px;
}
textarea {
width: 100%;
height: 50px;
padding: 5px;
border: 1px solid $color-grey-light;
background-color: $color-grey-lighter;
outline: 0;
}
}
.dayWishesButtonWrapper {
margin-bottom: 10px;
text-align: center;
}

View File

@ -1,9 +1,9 @@
import { shallowEqual, useSelector } from "react-redux" import { shallowEqual, useSelector } from "react-redux"
import { useCallback } from "react" import { useCallback } from "react"
import { selectUserJwtToken } from "../../../store/auth" import { selectUserJwtToken } from "../../store/auth"
import { AppState } from "../../../store" import { AppState } from "../../store"
import { fetchVolunteerDayWishesSet } from "../../../store/volunteerDayWishesSet" import { fetchVolunteerDayWishesSet } from "../../store/volunteerDayWishesSet"
import useAction from "../../../utils/useAction" import useAction from "../../utils/useAction"
const daysUtils = ["Jeudi", "Vendredi", "Samedi", "Dimanche", "Lundi"] const daysUtils = ["Jeudi", "Vendredi", "Samedi", "Dimanche", "Lundi"]
@ -43,3 +43,8 @@ export const useUserDayWishes = (): [any, any] => {
return [userWishes, saveWishes] return [userWishes, saveWishes]
} }
export const getDayLabel = (id: string): string => {
const matchingDay = daysChoice.find((day) => day.id === id)
return matchingDay ? matchingDay.label : ""
}

36
src/pages/Board/Board.tsx Normal file
View File

@ -0,0 +1,36 @@
import { FC, memo } from "react"
import { RouteComponentProps } from "react-router-dom"
import { useSelector } from "react-redux"
import { AppThunk } from "../../store"
import { fetchVolunteerDayWishesSetIfNeed } from "../../store/volunteerDayWishesSet"
import { selectUserJwtToken } from "../../store/auth"
import DayWishesForm from "../../components/VolunteerBoard/DayWishesForm/DayWishesForm"
import DDayInformations from "../../components/VolunteerBoard/DDayInformations/DDaysInformations"
import styles from "./styles.module.scss"
import DayWishes from "../../components/VolunteerBoard/DayWishes/DayWishes"
export type Props = RouteComponentProps
const BoardPage: FC<Props> = (): JSX.Element => {
const jwtToken = useSelector(selectUserJwtToken)
if (jwtToken === undefined) return <p>Loading...</p>
if (jwtToken) {
return (
<div className={styles.dayWishPage}>
<div className={styles.dayWisContent}>
<DayWishes />
<DayWishesForm />
<DDayInformations />
</div>
</div>
)
}
return <div>Besoin d&apos;être identifié</div>
}
// Fetch server-side data here
export const loadData = (): AppThunk[] => [fetchVolunteerDayWishesSetIfNeed()]
export default memo(BoardPage)

16
src/pages/Board/index.tsx Executable file
View File

@ -0,0 +1,16 @@
import loadable from "@loadable/component"
import { Loading, ErrorBoundary } from "../../components"
import { Props, loadData } from "./Board"
const BoardPage = loadable(() => import("./Board"), {
fallback: <Loading />,
})
export default (props: Props): JSX.Element => (
<ErrorBoundary>
<BoardPage {...props} />
</ErrorBoundary>
)
export { loadData }

View File

@ -0,0 +1,9 @@
@import "../../theme/mixins";
.dayWishPage {
@include page-wrapper-center;
}
.dayWisContent {
@include page-content-wrapper(800px);
}

View File

@ -5,7 +5,7 @@ import { useSelector } from "react-redux"
import { AppThunk } from "../../store" import { AppThunk } from "../../store"
import { fetchVolunteerDayWishesSetIfNeed } from "../../store/volunteerDayWishesSet" import { fetchVolunteerDayWishesSetIfNeed } from "../../store/volunteerDayWishesSet"
import { selectUserJwtToken } from "../../store/auth" import { selectUserJwtToken } from "../../store/auth"
import DayWishes from "../../components/VolunteerBoard/DayWishes/DayWishes" import DayWishes from "../../components/VolunteerBoard/DayWishesForm/DayWishesForm"
import DDayInformations from "../../components/VolunteerBoard/DDayInformations/DDaysInformations" import DDayInformations from "../../components/VolunteerBoard/DDayInformations/DDaysInformations"
import styles from "./styles.module.scss" import styles from "./styles.module.scss"

View File

@ -4,6 +4,7 @@ import App from "../app"
import AsyncHome, { loadData as loadHomeData } from "../pages/Home" import AsyncHome, { loadData as loadHomeData } from "../pages/Home"
import AsyncPreRegisterPage, { loadData as loadPreRegisterPage } from "../pages/PreRegister" import AsyncPreRegisterPage, { loadData as loadPreRegisterPage } from "../pages/PreRegister"
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 AsyncDayWishes, { loadData as loadDayWishesData } from "../pages/DayWishes" import AsyncDayWishes, { loadData as loadDayWishesData } from "../pages/DayWishes"
import AsyncTeamWishes, { loadData as loadTeamWishesData } from "../pages/TeamWishes" import AsyncTeamWishes, { loadData as loadTeamWishesData } from "../pages/TeamWishes"
import AsyncWish, { loadData as loadWishData } from "../pages/Wish" import AsyncWish, { loadData as loadWishData } from "../pages/Wish"
@ -60,6 +61,11 @@ export default [
component: AsyncWish, component: AsyncWish,
loadData: loadWishData, loadData: loadWishData,
}, },
{
path: "/board",
component: AsyncBoard,
loadData: loadBoardData,
},
{ {
component: NotFound, component: NotFound,
}, },

View File

@ -29,8 +29,19 @@
} }
} }
@mixin inner-content-wrapper() {
padding: 10px;
border-radius: 5px;
background-color: $color-grey-lighter;
}
@mixin clear-ul-style { @mixin clear-ul-style {
margin: 0; margin: 0;
padding: 0; padding: 0;
list-style: none; list-style: none;
} }
@mixin vertical-center {
top: 50%;
transform: translateY(-50%);
}

View File

@ -1,5 +1,6 @@
$color-white: #fff; $color-white: #fff;
$color-black: #000; $color-black: #000;
$color-red: #f00;
$color-orange: #ea4d0a; $color-orange: #ea4d0a;
$color-yellow: #fdd137; $color-yellow: #fdd137;
$color-grey-dark: #555; $color-grey-dark: #555;