diff --git a/package.json b/package.json index 1ee6bf0..5f3a283 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "@types/detect-node": "^2.0.0", "@types/js-cookie": "^3.0.1", "@types/lodash": "^4.14.177", + "@types/react-modal": "^3.13.1", "@types/serviceworker": "^0.0.32", "@types/web-push": "^3.3.2", "autoprefixer": "^10.2.6", @@ -110,6 +111,7 @@ "react": "^17.0.2", "react-dom": "^17.0.2", "react-helmet": "^6.1.0", + "react-modal": "^3.14.4", "react-redux": "^7.2.4", "react-router": "^5.2.0", "react-router-config": "^5.1.1", diff --git a/src/app/index.tsx b/src/app/index.tsx index 79deb2f..6ad4443 100755 --- a/src/app/index.tsx +++ b/src/app/index.tsx @@ -14,6 +14,8 @@ interface Route { route: { routes: RouteConfig[] } } +export const reactAppId = "react-view" + const App = ({ route }: Route): JSX.Element => (
diff --git a/src/components/Modal/Modal.tsx b/src/components/Modal/Modal.tsx new file mode 100644 index 0000000..f5ef499 --- /dev/null +++ b/src/components/Modal/Modal.tsx @@ -0,0 +1,38 @@ +import { FC, ReactNode, useCallback } from "react" +import RModal from "react-modal" +import { useSelector } from "react-redux" +import { reactAppId } from "../../app" +import styles from "./styles.module.scss" +import { hideModal, selectActiveModalId } from "../../store/ui" +import useAction from "../../utils/useAction" + +type Props = { + children: ReactNode + modalId: string +} + +RModal.setAppElement(`#${reactAppId}`) + +const Modal: FC = ({ modalId, children }): JSX.Element => { + const activeModalId = useSelector(selectActiveModalId) + const execHideModal = useAction(hideModal) + const onClose = useCallback(() => execHideModal(), [execHideModal]) + + const modalIsActive = activeModalId === modalId + + return ( + + + {children} + + ) +} + +export default Modal diff --git a/src/components/Modal/styles.module.scss b/src/components/Modal/styles.module.scss new file mode 100755 index 0000000..847b196 --- /dev/null +++ b/src/components/Modal/styles.module.scss @@ -0,0 +1,38 @@ +@import "../../theme/variables"; +@import "../../theme/mixins"; + +.modalOverlay { + position: fixed; + z-index: 100; + inset: 0; + background-color: rgba(0, 0, 0, 0.3); +} + +.modalContent { + @include horizontal-vertical-center(); + + position: absolute; + bottom: auto; + right: auto; + padding: 15px; + outline: 0; + background-color: $color-white; + border-radius: 15px; + border: $border-large; +} + +.closeButton { + position: absolute; + padding: 0; + z-index: 10; + top: 10px; + right: 10px; + width: 20px; + height: 20px; + border-radius: 0; + line-height: 20px; + color: $color-grey-dark; + text-align: center; + background: none; + cursor: pointer; +} diff --git a/src/components/VolunteerBoard/DayWishes/DayWishes.tsx b/src/components/VolunteerBoard/DayWishes/DayWishes.tsx index fe09fd4..700963c 100644 --- a/src/components/VolunteerBoard/DayWishes/DayWishes.tsx +++ b/src/components/VolunteerBoard/DayWishes/DayWishes.tsx @@ -1,19 +1,23 @@ -import { FC, memo } from "react" +import { FC, memo, useCallback } from "react" import get from "lodash/get" import styles from "./styles.module.scss" import { getDayLabel, useUserDayWishes } from "../days.utils" +import useAction from "../../../utils/useAction" +import { displayModal, MODAL_IDS } from "../../../store/ui" const DayWishes: FC = (): JSX.Element | null => { const [userWishes] = useUserDayWishes() const dayWishesString = get(userWishes, "dayWishes", []).map(getDayLabel).join(", ") const comment = get(userWishes, "dayWishesComment", "") + const execDisplayModal = useAction(displayModal) + const onEdit = useCallback(() => execDisplayModal(MODAL_IDS.DAYWISHES), [execDisplayModal]) return (
Mes jours de présence : {dayWishesString && {dayWishesString}} - {!dayWishesString && Non renseignés} + {!dayWishesString && Non renseignés}
{comment && (
@@ -24,7 +28,9 @@ const DayWishes: FC = (): JSX.Element | null => {
)}
- +
) diff --git a/src/components/VolunteerBoard/DayWishesForm/DayWishesForm.tsx b/src/components/VolunteerBoard/DayWishesForm/DayWishesForm.tsx index 3b9ece1..56eeda8 100644 --- a/src/components/VolunteerBoard/DayWishesForm/DayWishesForm.tsx +++ b/src/components/VolunteerBoard/DayWishesForm/DayWishesForm.tsx @@ -10,7 +10,11 @@ import { useUserDayWishes, } from "../days.utils" -const DayWishesForm: FC = (): JSX.Element | null => { +type Props = { + afterSubmit?: () => void | undefined +} + +const DayWishesForm: FC = ({ afterSubmit }): JSX.Element => { const [selection, setSelection] = useState(daysChoiceSelectionDefaultState) const commentRef = useRef(null) const [userWishes, saveWishes] = useUserDayWishes() @@ -41,7 +45,8 @@ const DayWishesForm: FC = (): JSX.Element | null => { const comment = get(commentRef, "current.value", "") const days = daysChoice.map(({ id }) => id).filter((id) => selection[id]) saveWishes(days, comment) - }, [selection, commentRef, saveWishes]) + if (afterSubmit) afterSubmit() + }, [selection, commentRef, saveWishes, afterSubmit]) return (
@@ -75,4 +80,8 @@ const DayWishesForm: FC = (): JSX.Element | null => { ) } +DayWishesForm.defaultProps = { + afterSubmit: undefined, +} + export default memo(DayWishesForm) diff --git a/src/components/VolunteerBoard/DayWishesForm/DayWishesFormModal.tsx b/src/components/VolunteerBoard/DayWishesForm/DayWishesFormModal.tsx new file mode 100644 index 0000000..e51d988 --- /dev/null +++ b/src/components/VolunteerBoard/DayWishesForm/DayWishesFormModal.tsx @@ -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 DayWishesForm from "./DayWishesForm" + +const DayWishesFormModal: FC = (): JSX.Element => { + const execHideModal = useAction(hideModal) + const afterFormSubmit = useCallback(() => execHideModal(), [execHideModal]) + + return ( + + + + ) +} + +export default memo(DayWishesFormModal) diff --git a/src/components/VolunteerBoard/DayWishesForm/styles.module.scss b/src/components/VolunteerBoard/DayWishesForm/styles.module.scss index d727f44..792a06b 100755 --- a/src/components/VolunteerBoard/DayWishesForm/styles.module.scss +++ b/src/components/VolunteerBoard/DayWishesForm/styles.module.scss @@ -6,7 +6,7 @@ } .dayWishesTitle { - padding: 4px; + padding: 15px 0; font-weight: bold; text-align: center; } diff --git a/src/pages/Board/Board.tsx b/src/pages/Board/Board.tsx index 842fcb6..73344c1 100644 --- a/src/pages/Board/Board.tsx +++ b/src/pages/Board/Board.tsx @@ -5,10 +5,10 @@ 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" +import DayWishesFormModal from "../../components/VolunteerBoard/DayWishesForm/DayWishesFormModal" export type Props = RouteComponentProps @@ -18,10 +18,10 @@ const BoardPage: FC = (): JSX.Element => { if (jwtToken === undefined) return

Loading...

if (jwtToken) { return ( -
-
+
+
- +
diff --git a/src/pages/Board/styles.module.scss b/src/pages/Board/styles.module.scss index f7b7660..116c1e4 100755 --- a/src/pages/Board/styles.module.scss +++ b/src/pages/Board/styles.module.scss @@ -1,9 +1,9 @@ @import "../../theme/mixins"; -.dayWishPage { +.boardPage { @include page-wrapper-center; } -.dayWisContent { +.boardContent { @include page-content-wrapper(800px); } diff --git a/src/pages/DayWishes/DayWishes.tsx b/src/pages/DayWishes/DayWishes.tsx index 6fb7638..dd737bf 100644 --- a/src/pages/DayWishes/DayWishes.tsx +++ b/src/pages/DayWishes/DayWishes.tsx @@ -5,7 +5,7 @@ import { useSelector } from "react-redux" import { AppThunk } from "../../store" import { fetchVolunteerDayWishesSetIfNeed } from "../../store/volunteerDayWishesSet" import { selectUserJwtToken } from "../../store/auth" -import DayWishes from "../../components/VolunteerBoard/DayWishesForm/DayWishesForm" +import DayWishesForm from "../../components/VolunteerBoard/DayWishesForm/DayWishesForm" import DDayInformations from "../../components/VolunteerBoard/DDayInformations/DDaysInformations" import styles from "./styles.module.scss" @@ -18,8 +18,8 @@ const HomePage: FC = (): JSX.Element => { if (jwtToken) { return (
-
- +
+
diff --git a/src/pages/DayWishes/styles.module.scss b/src/pages/DayWishes/styles.module.scss index f7b7660..a870e16 100755 --- a/src/pages/DayWishes/styles.module.scss +++ b/src/pages/DayWishes/styles.module.scss @@ -4,6 +4,6 @@ @include page-wrapper-center; } -.dayWisContent { +.dayWishContent { @include page-content-wrapper(800px); } diff --git a/src/store/rootReducer.ts b/src/store/rootReducer.ts index 329f8ac..beb3aa8 100644 --- a/src/store/rootReducer.ts +++ b/src/store/rootReducer.ts @@ -6,6 +6,7 @@ import javGameList from "./javGameList" import preVolunteerAdd from "./preVolunteerAdd" import preVolunteerCount from "./preVolunteerCount" import teamList from "./teamList" +import ui from "./ui" import volunteer from "./volunteer" import volunteerAdd from "./volunteerAdd" import volunteerList from "./volunteerList" @@ -27,6 +28,7 @@ export default (history: History) => ({ preVolunteerAdd, preVolunteerCount, teamList, + ui, volunteer, volunteerAdd, volunteerList, diff --git a/src/store/ui.ts b/src/store/ui.ts new file mode 100644 index 0000000..333f968 --- /dev/null +++ b/src/store/ui.ts @@ -0,0 +1,31 @@ +import { createSelector, createSlice, PayloadAction } from "@reduxjs/toolkit" +import { AppState } from "./index" + +const initialState = { + modalId: "", +} + +const uiSlice = createSlice({ + name: "ui", + initialState, + reducers: { + displayModal: (state, action: PayloadAction) => { + state.modalId = action.payload + }, + hideModal: (state) => { + state.modalId = "" + }, + }, +}) + +export default uiSlice.reducer + +export const { displayModal, hideModal } = uiSlice.actions + +const selectUiData = (state: AppState) => state.ui + +export const selectActiveModalId = createSelector(selectUiData, (ui) => ui.modalId) + +export const MODAL_IDS = { + DAYWISHES: "DAYWISHES", +} diff --git a/src/theme/mixins.scss b/src/theme/mixins.scss index 8426a63..13c6207 100644 --- a/src/theme/mixins.scss +++ b/src/theme/mixins.scss @@ -45,3 +45,9 @@ top: 50%; transform: translateY(-50%); } + +@mixin horizontal-vertical-center { + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} diff --git a/yarn.lock b/yarn.lock index 806a401..8e4dc07 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1872,6 +1872,13 @@ dependencies: "@types/react" "*" +"@types/react-modal@^3.13.1": + version "3.13.1" + resolved "https://registry.yarnpkg.com/@types/react-modal/-/react-modal-3.13.1.tgz#5b9845c205fccc85d9a77966b6e16dc70a60825a" + integrity sha512-iY/gPvTDIy6Z+37l+ibmrY+GTV4KQTHcCyR5FIytm182RQS69G5ps4PH2FxtC7bAQ2QRHXMevsBgck7IQruHNg== + dependencies: + "@types/react" "*" + "@types/react-redux@^7.1.20": version "7.1.20" resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.20.tgz#42f0e61ababb621e12c66c96dda94c58423bd7df" @@ -4718,6 +4725,11 @@ executable@^4.1.0: dependencies: pify "^2.2.0" +exenv@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/exenv/-/exenv-1.2.2.tgz#2ae78e85d9894158670b03d47bec1f03bd91bb9d" + integrity sha1-KueOhdmJQVhnCwPUe+wfA72Ru50= + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -7364,7 +7376,7 @@ longest@^1.0.0: resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc= -loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== @@ -9052,6 +9064,21 @@ react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== +react-lifecycles-compat@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + +react-modal@^3.14.4: + version "3.14.4" + resolved "https://registry.yarnpkg.com/react-modal/-/react-modal-3.14.4.tgz#2ca7e8e9a180955e5c9508c228b73167c1e6f6a3" + integrity sha512-8surmulejafYCH9wfUmFyj4UfbSJwjcgbS9gf3oOItu4Hwd6ivJyVBETI0yHRhpJKCLZMUtnhzk76wXTsNL6Qg== + dependencies: + exenv "^1.2.0" + prop-types "^15.7.2" + react-lifecycles-compat "^3.0.0" + warning "^4.0.3" + react-redux@^7.2.4: version "7.2.6" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.6.tgz#49633a24fe552b5f9caf58feb8a138936ddfe9aa" @@ -11003,6 +11030,13 @@ walker@^1.0.7: dependencies: makeerror "1.0.12" +warning@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + watchpack@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.2.0.tgz#47d78f5415fe550ecd740f99fe2882323a58b1ce"