mirror of
https://github.com/Paris-est-Ludique/intranet.git
synced 2025-06-08 08:34:20 +02:00
Add TeamWishes form and display components to Board
This commit is contained in:
parent
733c4fc04f
commit
6e22136737
@ -15,6 +15,7 @@
|
||||
bottom: auto;
|
||||
right: auto;
|
||||
padding: 15px;
|
||||
max-height: 80vh;
|
||||
outline: 0;
|
||||
background-color: $color-white;
|
||||
border-radius: 15px;
|
||||
|
@ -3,6 +3,8 @@ import DayWishes from "./DayWishes/DayWishes"
|
||||
import DayWishesFormModal from "./DayWishesForm/DayWishesFormModal"
|
||||
import ParticipationDetails from "./ParticipationDetails/ParticipationDetails"
|
||||
import ParticipationDetailsFormModal from "./ParticipationDetailsForm/ParticipationDetailsFormModal"
|
||||
import TeamWishes from "./TeamWishes/TeamWishes"
|
||||
import TeamWishesFormModal from "./TeamWishesForm/TeamWishesFormModal"
|
||||
import withUserConnected from "../../utils/withUserConnected"
|
||||
|
||||
const Board: FC = (): JSX.Element => (
|
||||
@ -11,6 +13,8 @@ const Board: FC = (): JSX.Element => (
|
||||
<DayWishesFormModal />
|
||||
<ParticipationDetails />
|
||||
<ParticipationDetailsFormModal />
|
||||
<TeamWishes />
|
||||
<TeamWishesFormModal />
|
||||
</div>
|
||||
)
|
||||
|
||||
|
@ -22,9 +22,7 @@ const DayWishes: FC = (): JSX.Element | null => {
|
||||
{comment && (
|
||||
<div className={styles.commentLine}>
|
||||
<span className={styles.commentLineTitle}>Mon commentaire :</span>
|
||||
<span className={styles.commentLineText}>
|
||||
{get(userWishes, "dayWishesComment", "")}
|
||||
</span>
|
||||
<span className={styles.commentLineText}>{comment}</span>
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.editButton}>
|
||||
|
@ -9,7 +9,7 @@ type Props = {
|
||||
afterSubmit?: () => void | undefined
|
||||
}
|
||||
|
||||
const ParticipationDetailsForm: FC<Props> = (): JSX.Element | null => {
|
||||
const ParticipationDetails: FC<Props> = (): JSX.Element | null => {
|
||||
const [participationDetails] = useUserParticipationDetails()
|
||||
const age = get(participationDetails, "age", "")
|
||||
const tShirtSize = get(participationDetails, "teeshirtSize", "")
|
||||
@ -44,4 +44,4 @@ const ParticipationDetailsForm: FC<Props> = (): JSX.Element | null => {
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(ParticipationDetailsForm)
|
||||
export default memo(ParticipationDetails)
|
||||
|
51
src/components/VolunteerBoard/TeamWishes/TeamWishes.tsx
Normal file
51
src/components/VolunteerBoard/TeamWishes/TeamWishes.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import { FC, memo, useCallback } from "react"
|
||||
import { useSelector } from "react-redux"
|
||||
import get from "lodash/get"
|
||||
import styles from "./styles.module.scss"
|
||||
import { displayModal, MODAL_IDS } from "../../../store/ui"
|
||||
import useAction from "../../../utils/useAction"
|
||||
import { useUserTeamWishes } from "../teamWishes.utils"
|
||||
import { selectTeamList } from "../../../store/teamList"
|
||||
|
||||
type Props = {
|
||||
afterSubmit?: () => void | undefined
|
||||
}
|
||||
|
||||
const TeamWishes: FC<Props> = (): JSX.Element | null => {
|
||||
const teams = useSelector(selectTeamList)
|
||||
const [teamWishesData] = useUserTeamWishes()
|
||||
const teamWishesString = get(teamWishesData, "teamWishes", [])
|
||||
.map((id: number): string =>
|
||||
get(
|
||||
teams.find((team) => team && team.id === id),
|
||||
"name",
|
||||
""
|
||||
)
|
||||
)
|
||||
.filter((name: string) => name)
|
||||
.join(", ")
|
||||
const comment = get(teamWishesData, "teamWishesComment", "")
|
||||
const execDisplayModal = useAction(displayModal)
|
||||
const onEdit = useCallback(() => execDisplayModal(MODAL_IDS.TEAMWISHES), [execDisplayModal])
|
||||
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<div className={styles.title}>Mes choix d'équipes</div>
|
||||
{teamWishesString && <span>{teamWishesString}</span>}
|
||||
{!teamWishesString && <span className={styles.lineEmpty}>Non renseignés</span>}
|
||||
{comment && (
|
||||
<div className={styles.commentLine}>
|
||||
<span className={styles.commentLineTitle}>Mon commentaire :</span>
|
||||
<span className={styles.commentLineText}>{comment}</span>
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.editButton}>
|
||||
<button type="button" onClick={onEdit}>
|
||||
Modifier
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(TeamWishes)
|
44
src/components/VolunteerBoard/TeamWishes/styles.module.scss
Executable file
44
src/components/VolunteerBoard/TeamWishes/styles.module.scss
Executable file
@ -0,0 +1,44 @@
|
||||
@import "../../../theme/variables";
|
||||
@import "../../../theme/mixins";
|
||||
|
||||
.root {
|
||||
@include inner-content-wrapper();
|
||||
|
||||
position: relative;
|
||||
padding-right: 130px;
|
||||
}
|
||||
|
||||
.title {
|
||||
padding-bottom: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.line {
|
||||
margin: 2px 0;
|
||||
}
|
||||
|
||||
.lineEmpty {
|
||||
color: $color-red;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.commentLine {
|
||||
span {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.commentLineTitle {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.commentLineText {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.editButton {
|
||||
@include vertical-center();
|
||||
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
import { FC, memo, useCallback, useEffect, useRef, useState } from "react"
|
||||
import { useSelector } from "react-redux"
|
||||
import get from "lodash/get"
|
||||
import set from "lodash/set"
|
||||
import classnames from "classnames"
|
||||
import styles from "./styles.module.scss"
|
||||
import { useUserTeamWishes } from "../teamWishes.utils"
|
||||
import { selectTeamList } from "../../../store/teamList"
|
||||
import useSelection from "../useSelection"
|
||||
|
||||
type Props = {
|
||||
afterSubmit?: () => void | undefined
|
||||
}
|
||||
|
||||
const TeamWishesForm: FC<Props> = ({ afterSubmit }): JSX.Element | null => {
|
||||
const teams = useSelector(selectTeamList)
|
||||
const { addToSelection, toggleToSelection, isInSelection } = useSelection()
|
||||
const commentRef = useRef<HTMLTextAreaElement | null>(null)
|
||||
const [userWishes, saveWishes] = useUserTeamWishes()
|
||||
const [extendedTeam, setExtendedTeam] = useState<number | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (!userWishes) return
|
||||
addToSelection(...get(userWishes, "teamWishes", []))
|
||||
set(commentRef, "current.value", get(userWishes, "teamWishesComment", ""))
|
||||
}, [userWishes, addToSelection])
|
||||
|
||||
const onTeamClick = useCallback((id) => toggleToSelection(id), [toggleToSelection])
|
||||
|
||||
const onExtendClick = useCallback(
|
||||
(id) => setExtendedTeam(extendedTeam === id ? null : id),
|
||||
[extendedTeam, setExtendedTeam]
|
||||
)
|
||||
|
||||
console.log("extendedTeam", extendedTeam)
|
||||
|
||||
const onSubmit = useCallback(() => {
|
||||
const teamWishesComment = get(commentRef, "current.value", "")
|
||||
const teamWishes = teams
|
||||
.map((team) => team && team.id)
|
||||
.filter((id) => id && isInSelection(id))
|
||||
saveWishes({ teamWishes, teamWishesComment })
|
||||
if (afterSubmit) afterSubmit()
|
||||
}, [teams, isInSelection, saveWishes, afterSubmit])
|
||||
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<div className={styles.title}>Mes choix d'équipes</div>
|
||||
<ul className={styles.teamList}>
|
||||
{teams.map((team: any) => (
|
||||
<li
|
||||
key={team.id}
|
||||
className={classnames(
|
||||
styles.teamLine,
|
||||
isInSelection(team.id) && styles.active,
|
||||
extendedTeam === team.id && styles.extended
|
||||
)}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onTeamClick(team.id)}
|
||||
className={styles.teamButton}
|
||||
>
|
||||
{team.name}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onExtendClick(team.id)}
|
||||
className={styles.extendButton}
|
||||
>
|
||||
Détails
|
||||
</button>
|
||||
<div className={styles.teamDescription}>{team.description}</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className={styles.commentWrapper}>
|
||||
<label htmlFor="day-choice-comment">Un commentaire, une précision ?</label>
|
||||
<textarea id="day-choice-comment" ref={commentRef} />
|
||||
</div>
|
||||
<div className={styles.buttonWrapper}>
|
||||
<button type="submit" onClick={onSubmit}>
|
||||
Enregistrer
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
TeamWishesForm.defaultProps = {
|
||||
afterSubmit: undefined,
|
||||
}
|
||||
|
||||
export default memo(TeamWishesForm)
|
@ -0,0 +1,18 @@
|
||||
import { FC, memo, useCallback } from "react"
|
||||
import { hideModal, MODAL_IDS } from "../../../store/ui"
|
||||
import Modal from "../../Modal/Modal"
|
||||
import useAction from "../../../utils/useAction"
|
||||
import TeamWishesForm from "./TeamWishesForm"
|
||||
|
||||
const TeamWishesFormModal: FC = (): JSX.Element => {
|
||||
const execHideModal = useAction(hideModal)
|
||||
const afterFormSubmit = useCallback(() => execHideModal(), [execHideModal])
|
||||
|
||||
return (
|
||||
<Modal modalId={MODAL_IDS.TEAMWISHES}>
|
||||
<TeamWishesForm afterSubmit={afterFormSubmit} />
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(TeamWishesFormModal)
|
100
src/components/VolunteerBoard/TeamWishesForm/styles.module.scss
Executable file
100
src/components/VolunteerBoard/TeamWishesForm/styles.module.scss
Executable file
@ -0,0 +1,100 @@
|
||||
@import "../../../theme/variables";
|
||||
@import "../../../theme/mixins";
|
||||
|
||||
.root {
|
||||
width: 470px;
|
||||
}
|
||||
|
||||
.title {
|
||||
padding: 4px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.inputWrapper {
|
||||
margin: 10px 0;
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
width: 170px;
|
||||
}
|
||||
input {
|
||||
width: 300px;
|
||||
border: 1px solid $color-grey-medium;
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.teamList {
|
||||
@include clear-ul-style;
|
||||
}
|
||||
|
||||
.teamLine {
|
||||
position: relative;
|
||||
margin: 4px 0;
|
||||
background-color: $color-grey-lighter;
|
||||
}
|
||||
|
||||
.teamButton {
|
||||
display: block;
|
||||
padding: 4px 6px;
|
||||
background: none;
|
||||
color: $color-grey-medium;
|
||||
border-radius: 0;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
|
||||
.active & {
|
||||
background-color: $color-black;
|
||||
color: $color-yellow;
|
||||
}
|
||||
}
|
||||
|
||||
.extendButton {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 4px;
|
||||
padding: 2px;
|
||||
background: none;
|
||||
color: $color-grey-dark;
|
||||
border-radius: 0;
|
||||
font-size: 0.9em;
|
||||
font-weight: normal;
|
||||
|
||||
.active & {
|
||||
color: $color-grey-light;
|
||||
}
|
||||
}
|
||||
|
||||
.teamDescription {
|
||||
display: none;
|
||||
padding: 2px 4px 6px 8px;
|
||||
color: $color-grey-dark;
|
||||
font-size: 0.95em;
|
||||
|
||||
.extended & {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.commentWrapper {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
.buttonWrapper {
|
||||
margin-bottom: 10px;
|
||||
text-align: center;
|
||||
}
|
29
src/components/VolunteerBoard/teamWishes.utils.ts
Normal file
29
src/components/VolunteerBoard/teamWishes.utils.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { useCallback } from "react"
|
||||
import { shallowEqual, useSelector } from "react-redux"
|
||||
import useAction from "../../utils/useAction"
|
||||
import { selectUserJwtToken } from "../../store/auth"
|
||||
import { AppState } from "../../store"
|
||||
import { fetchVolunteerTeamWishesSet } from "../../store/volunteerTeamWishesSet"
|
||||
|
||||
export const useUserTeamWishes = (): [any, any] => {
|
||||
const save = useAction(fetchVolunteerTeamWishesSet)
|
||||
const jwtToken = useSelector(selectUserJwtToken)
|
||||
const userTeamWishes = useSelector(
|
||||
(state: AppState) => state.volunteerTeamWishesSet?.entity,
|
||||
shallowEqual
|
||||
)
|
||||
|
||||
const saveTeamWishes = useCallback(
|
||||
({ teamWishes, teamWishesComment }) => {
|
||||
if (!userTeamWishes) return
|
||||
save(jwtToken, 0, {
|
||||
id: userTeamWishes.id,
|
||||
teamWishes,
|
||||
teamWishesComment,
|
||||
})
|
||||
},
|
||||
[userTeamWishes, save, jwtToken]
|
||||
)
|
||||
|
||||
return [userTeamWishes, saveTeamWishes]
|
||||
}
|
87
src/components/VolunteerBoard/useSelection.ts
Normal file
87
src/components/VolunteerBoard/useSelection.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import { useCallback, useMemo, useReducer } from "react"
|
||||
|
||||
type valueType = string | number
|
||||
|
||||
type selectionType = {
|
||||
[key: string]: boolean
|
||||
}
|
||||
|
||||
type State = {
|
||||
selection: selectionType
|
||||
}
|
||||
|
||||
type Action = { type: "add"; payload: valueType[] } | { type: "toggle"; payload: valueType }
|
||||
|
||||
interface selectionHook {
|
||||
addToSelection: (...values: valueType[]) => void
|
||||
toggleToSelection: (value: valueType) => void
|
||||
isInSelection: (value: valueType) => boolean
|
||||
}
|
||||
|
||||
const initialState: State = {
|
||||
selection: {},
|
||||
}
|
||||
|
||||
const buildIndex = (value: valueType) => `item_${value}`
|
||||
|
||||
const reducer = (state: State, action: Action): State => {
|
||||
switch (action.type) {
|
||||
case "add": {
|
||||
const values = action.payload
|
||||
return {
|
||||
selection: values.reduce(
|
||||
(acc: selectionType, value: valueType) => ({
|
||||
...acc,
|
||||
[buildIndex(value)]: true,
|
||||
}),
|
||||
state.selection
|
||||
),
|
||||
}
|
||||
}
|
||||
case "toggle": {
|
||||
const value = action.payload
|
||||
const index = buildIndex(value)
|
||||
return {
|
||||
selection: {
|
||||
...state.selection,
|
||||
[index]: !state.selection[index],
|
||||
},
|
||||
}
|
||||
}
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
const useSelection = (): selectionHook => {
|
||||
const [state, dispatch] = useReducer(reducer, initialState)
|
||||
|
||||
const addToSelection = useCallback(
|
||||
(...values: valueType[]) => {
|
||||
dispatch({ type: "add", payload: values })
|
||||
},
|
||||
[dispatch]
|
||||
)
|
||||
|
||||
const toggleToSelection = useCallback(
|
||||
(value: valueType) => {
|
||||
dispatch({ type: "toggle", payload: value })
|
||||
},
|
||||
[dispatch]
|
||||
)
|
||||
|
||||
const isInSelection = useCallback(
|
||||
(value: valueType) => {
|
||||
const index = buildIndex(value)
|
||||
return state.selection[index]
|
||||
},
|
||||
[state.selection]
|
||||
)
|
||||
|
||||
return useMemo(
|
||||
() => ({ addToSelection, toggleToSelection, isInSelection }),
|
||||
[addToSelection, toggleToSelection, isInSelection]
|
||||
)
|
||||
}
|
||||
|
||||
export default useSelection
|
@ -3,11 +3,13 @@ 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 Page from "../../components/Page/Page"
|
||||
import Board from "../../components/VolunteerBoard/Board"
|
||||
import { fetchVolunteerDayWishesSetIfNeed } from "../../store/volunteerDayWishesSet"
|
||||
import { fetchVolunteerParticipationDetailsSetIfNeed } from "../../store/volunteerParticipationDetailsSet"
|
||||
import { fetchVolunteerTeamWishesSetIfNeed } from "../../store/volunteerTeamWishesSet"
|
||||
import { fetchTeamListIfNeed } from "../../store/teamList"
|
||||
|
||||
export type Props = RouteComponentProps
|
||||
|
||||
@ -29,6 +31,8 @@ const BoardPage: FC<Props> = (): JSX.Element => {
|
||||
export const loadData = (): AppThunk[] => [
|
||||
fetchVolunteerDayWishesSetIfNeed(),
|
||||
fetchVolunteerParticipationDetailsSetIfNeed(),
|
||||
fetchVolunteerTeamWishesSetIfNeed(),
|
||||
fetchTeamListIfNeed(),
|
||||
]
|
||||
|
||||
export default memo(BoardPage)
|
||||
|
@ -29,4 +29,5 @@ export const selectActiveModalId = createSelector(selectUiData, (ui) => ui.modal
|
||||
export const MODAL_IDS = {
|
||||
DAYWISHES: "DAYWISHES",
|
||||
PARTICIPATIONDETAILS: "PARTICIPATIONDETAILS",
|
||||
TEAMWISHES: "TEAMWISHES",
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user