diff --git a/docker-compose.local.yml b/docker-compose.local.yml index e598070da4096621b4e3a18dddfd99e293998998..bb3f992e2f9fd94563396ca2db5909074611ee26 100644 --- a/docker-compose.local.yml +++ b/docker-compose.local.yml @@ -10,10 +10,16 @@ services: - ./nginx/site.conf:/etc/nginx/conf.d/default.conf - ./cert.pem:/etc/nginx/cert.pem - ./key.pem:/etc/nginx/key.pem + - ./../${IMAGE_FOLDER}:/usr/share/nginx/html/lib/${IMAGE_FOLDER} ports: - 443:443 depends_on: - backend + environment: + - IMAGE_FOLDER=${IMAGE_FOLDER} + # For linux users + # extra_hosts: + # - "host.docker.internal:host-gateway" database: image: mysql:5 @@ -39,6 +45,7 @@ services: - ./configs:/app/configs - ./letsencrypt_cache:/app/letsencrypt_cache - ./data:/app/data + - ./../${IMAGE_FOLDER}:/app/${IMAGE_FOLDER} ports: - ${HTTPS_PORT}:${HTTPS_PORT} - 8090:8090 @@ -57,3 +64,5 @@ services: - DATABASE_NAME=${DATABASE_NAME} - DATABASE_PASSWORD=${DATABASE_PASSWORD} - DATABASE_HOST=database + - MOCK_OAUTH2=${MOCK_OAUTH2} + - IMAGE_FOLDER=${IMAGE_FOLDER} diff --git a/nginx/site.conf b/nginx/site.conf index 96266e857d0e288d2de32fd2a5fcf1a52372ca7b..2ab117ce14c72d39f31bf5a03a0e34e40575d7aa 100644 --- a/nginx/site.conf +++ b/nginx/site.conf @@ -24,4 +24,10 @@ server { location /swagger { proxy_pass https://backend:1443/swagger; } + location ~ ^/assets/(.+\.(?:gif|jpe?g|svg))$ { + alias /usr/share/nginx/html/lib/$1; + gzip_static on; + expires max; + add_header Cache-Control public; + } } \ No newline at end of file diff --git a/package.json b/package.json index e8a7c9e485a14068ec00d92b2e0d40018a3c8f1d..5dc3567c83d5412f1d1f0a144f3805588bf786d2 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,8 @@ "scripts": { "start": "react-scripts start", "build": "react-scripts build", - "local-up" : "docker-compose -f docker-compose.local.yml up -d && yarn start", - "local-down" : "docker-compose -f docker-compose.local.yml down", + "local-up": "docker-compose -f docker-compose.local.yml up -d && yarn start", + "local-down": "docker-compose -f docker-compose.local.yml down", "docker": "docker run -it --rm -p 3000:3000 front-backoffice", "lint": "eslint .", "lint:fix": "eslint --fix .", @@ -51,6 +51,8 @@ "prettier": "^2.3.2" }, "dependencies": { + "@material-ui/core": "^4.12.3", + "@material-ui/lab": "^4.0.0-alpha.60", "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", "@testing-library/user-event": "^12.1.10", diff --git a/src/components/Editing/Editing.tsx b/src/components/Editing/Editing.tsx index 386b27dd14ab50de82c6a5b39b5b5c7165f3a08e..90158488aeafb84ee5759ae8e9b6386dd4a94747 100644 --- a/src/components/Editing/Editing.tsx +++ b/src/components/Editing/Editing.tsx @@ -18,6 +18,7 @@ const Editing: React.FC = () => { const [date, setDate] = useState<Date>(new Date()) const [info, setInfo] = useState<string>('') const [title, setTitle] = useState<string>('') + const [imageURL, setImageURL] = useState<string>('') const [content, setContent] = useState<string>('') const [question, setQuestion] = useState<string>('') const [link, setLink] = useState<string>('') @@ -32,7 +33,12 @@ const Editing: React.FC = () => { const handleSaveMonthlyInfo = async (): Promise<void> => { if (user) { const newsletterService = new NewsletterService() - await newsletterService.saveMonthlyInfo(date, info, user.xsrftoken) + await newsletterService.saveMonthlyInfo( + date, + info, + imageURL, + user.xsrftoken + ) setIsTouched(false) } } @@ -112,6 +118,7 @@ const Editing: React.FC = () => { title !== '' || content !== '' || question !== '' || + imageURL !== '' || link !== '') && isTouched ) { @@ -121,7 +128,7 @@ const Editing: React.FC = () => { const handleEditorChange = ( value: string, - type: 'info' | 'title' | 'content' | 'question' | 'link' + type: 'info' | 'title' | 'content' | 'question' | 'link' | 'image' ): void => { setIsTouched(true) if (type === 'info') { @@ -139,8 +146,12 @@ const Editing: React.FC = () => { if (type === 'link') { setLink(value) } + if (type === 'image') { + setImageURL(value) + } } const resetFields = useCallback(() => { + setImageURL('') setInfo('') setTitle('') setContent('') @@ -174,6 +185,7 @@ const Editing: React.FC = () => { ) if (montlhyInfo) { setInfo(montlhyInfo.info) + setImageURL(montlhyInfo.image) setIsTouched(false) } if (montlhyNews) { @@ -214,6 +226,7 @@ const Editing: React.FC = () => { onCancel={handleCancel} handleChange={handleEditorChange} onDelete={handleOpenDeleteModal} + imageURL={imageURL} /> <hr /> <MonthlyNews diff --git a/src/components/Editing/editing.scss b/src/components/Editing/editing.scss index 4987046c8f117b64586dbd488290b8f3c617aa1a..82c9a2f6d355c2971345c6476438f3e022f2caac 100644 --- a/src/components/Editing/editing.scss +++ b/src/components/Editing/editing.scss @@ -1,5 +1,7 @@ @import '../../styles/config/typography.scss'; @import '../../styles/config/layout.scss'; +@import '../../styles/config/breakpoints'; + .header { position: fixed; z-index: 1500; @@ -26,4 +28,10 @@ hr { } .buttons { display: flex; + @media screen and(max-width: $width-tablet) { + flex-direction: column; + button { + width: 100%; + } + } } diff --git a/src/components/ImagePicker/ImagePicker.tsx b/src/components/ImagePicker/ImagePicker.tsx new file mode 100644 index 0000000000000000000000000000000000000000..fa93461a1958d0ec3f5cfd95f76d2481ab81f82b --- /dev/null +++ b/src/components/ImagePicker/ImagePicker.tsx @@ -0,0 +1,133 @@ +import React, { useContext, useEffect, useState } from 'react' +import { NewsletterService } from '../../services/newsletter.service' +import Modal from '../Modal/Modal' +import Pagination from '@material-ui/lab/Pagination' +import SingleImage from './SingleImage' +import { UserContext, UserContextProps } from '../../hooks/userContext' + +interface ImagePickerProps { + imageURL: string + handleChange: ( + value: string, + type: 'info' | 'title' | 'content' | 'question' | 'link' | 'image' + ) => void +} + +const ImagePicker: React.FC<ImagePickerProps> = ({ + imageURL, + handleChange, +}: ImagePickerProps) => { + const [imageNames, setImageNames] = useState<Array<string[]>>([]) + const [selectedImageURL, setSelectedImageURL] = useState<string>( + imageURL && imageURL !== null ? imageURL : '' + ) + const [openModal, setOpenModal] = useState<boolean>(false) + const { user }: Partial<UserContextProps> = useContext(UserContext) + const [currentPage, setCurrentPage] = useState(1) + const [pageCount, setpageCount] = useState<number>(1) + const [preSelectImage, setPreSelectImage] = useState<string>('') + let imagePerPage = 10 + + const toggleModal = () => { + setOpenModal((prev) => !prev) + } + const handleChangePage = (page: number) => { + setCurrentPage(page) + } + const handleValidateImage = () => { + setSelectedImageURL(preSelectImage) + handleChange(preSelectImage, 'image') + setOpenModal(false) + } + + useEffect(() => { + let subscribed = true + async function getImageNames() { + if (user) { + const newsletterService = new NewsletterService() + const images = await newsletterService.getEcogestureImages( + user.xsrftoken + ) + //Split array depending on page numbers + setpageCount(Math.ceil(images.length / imagePerPage) - 1) + const arraySplitted = [] + while (images.length) { + arraySplitted.push(images.splice(0, imagePerPage)) + } + setImageNames(arraySplitted) + } + } + if (subscribed) { + getImageNames() + } + return () => { + subscribed = false + } + }, [user, imagePerPage]) + + return ( + <> + {selectedImageURL === '' || !selectedImageURL ? ( + <> + <p>Pas d'image sélectionnée</p> + <button className="btnValid" onClick={toggleModal}> + Choisir une image + </button> + </> + ) : ( + <> + <img + src={selectedImageURL} + width="120" + height="120" + className="ecogesture-image" + alt="selected" + /> + <button className="btnDelete" onClick={toggleModal}> + Modifier l'image + </button> + </> + )} + + {openModal && ( + <Modal classname={'modal-large'}> + <> + <div className="image-picker"> + {imageNames && + imageNames !== [] && + imageNames[currentPage] !== [] && + imageNames[currentPage].map((imageURL) => ( + <SingleImage + imageURL={imageURL} + key={imageURL} + selectedImage={preSelectImage} + setSelectedImageURL={setPreSelectImage} + /> + ))} + </div> + <div className="footer"> + <Pagination + count={pageCount} + siblingCount={0} + onChange={(e, page) => handleChangePage(page)} + /> + <div className="buttons"> + <button + className="btnCancel" + onClick={() => setOpenModal(false)} + > + Annuler + </button> + <button className="btnValid" onClick={handleValidateImage}> + Valider + </button> + </div> + </div> + </> + </Modal> + )} + </> + ) +} + +export default ImagePicker diff --git a/src/components/ImagePicker/SingleImage.tsx b/src/components/ImagePicker/SingleImage.tsx new file mode 100644 index 0000000000000000000000000000000000000000..b217b1b192e1e16509615eadd41b5c5ac070a42e --- /dev/null +++ b/src/components/ImagePicker/SingleImage.tsx @@ -0,0 +1,33 @@ +import React from 'react' +import './imagePicker.scss' +interface SingleImageProps { + imageURL: string + selectedImage: string + setSelectedImageURL: React.Dispatch<React.SetStateAction<string>> +} + +const SingleImage: React.FC<SingleImageProps> = ({ + imageURL, + selectedImage, + setSelectedImageURL, +}: SingleImageProps) => { + const selectImage = (imageURL: string) => { + setSelectedImageURL(imageURL) + } + return ( + <img + src={imageURL} + width="120" + height="120" + className={ + selectedImage === imageURL + ? 'ecogesture-image selected' + : 'ecogesture-image' + } + alt={'ecogesture' + imageURL.replace(/^\D+/g, '')} + onClick={() => selectImage(imageURL)} + /> + ) +} + +export default SingleImage diff --git a/src/components/ImagePicker/imagePicker.scss b/src/components/ImagePicker/imagePicker.scss new file mode 100644 index 0000000000000000000000000000000000000000..2d1b5855e199da348c0bd4f4afc418f1c42dc837 --- /dev/null +++ b/src/components/ImagePicker/imagePicker.scss @@ -0,0 +1,30 @@ +@import '../../styles/config/colors'; +@import '../../styles/config/breakpoints'; + +.image-picker { + display: flex; + flex-wrap: wrap; + justify-content: center; + @media screen and(max-width: $width-tablet) { + .ecogesture-image { + max-width: 80px; + max-height: 80px; + } + } +} +.ecogesture-image { + display: block; + box-sizing: border-box; + border: solid 1px transparent; + padding: 0.5rem; + transition: all 300ms ease; + border-radius: 5px; + background: $dark-light; + margin: 0.3rem; +} +.selected { + border: solid 1px $gold; +} +.footer { + margin-top: auto; +} diff --git a/src/components/Modal/Modal.tsx b/src/components/Modal/Modal.tsx index fde74896a3e976c5e45170db36803d4a572b772e..7d7c146bfb054b64e141bb9487b150380f4c240a 100644 --- a/src/components/Modal/Modal.tsx +++ b/src/components/Modal/Modal.tsx @@ -3,13 +3,14 @@ import { createPortal } from 'react-dom' import './modal.scss' interface ModalProps { children: ReactChild + classname?: string } -const Modal: React.FC<ModalProps> = ({ children }: ModalProps) => { +const Modal: React.FC<ModalProps> = ({ children, classname }: ModalProps) => { return createPortal( <div className="modal-bg"> <div className="modal-container"> - <div className="modal-content">{children}</div> + <div className={`modal-content ${classname}`}>{children}</div> </div> </div>, document.body diff --git a/src/components/Modal/modal.scss b/src/components/Modal/modal.scss index bf0f5ffd96635f667ccf8f78ddfbbcbba256cb4e..7ef20b52e3b26a39bb6db6d21106bc564f647bfc 100644 --- a/src/components/Modal/modal.scss +++ b/src/components/Modal/modal.scss @@ -27,6 +27,12 @@ max-width: 350px; width: 100%; } + .modal-large { + max-width: 700px; + min-height: 380px; + display: flex; + flex-direction: column; + } .buttons { display: flex; justify-content: center; diff --git a/src/components/MonthlyInfo/MonthlyInfo.tsx b/src/components/MonthlyInfo/MonthlyInfo.tsx index f41d545f253c31b6d2f03a15e7f60e4b6d457474..99caef2b05c7f154f35a6a56b4a6ac424cb1f68f 100644 --- a/src/components/MonthlyInfo/MonthlyInfo.tsx +++ b/src/components/MonthlyInfo/MonthlyInfo.tsx @@ -4,15 +4,17 @@ import { convertStringToEditorState } from '../../utils/editorStateManagment' import CustomEditor from '../Editing/CustomEditor' import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css' import './monthlyInfo.scss' +import ImagePicker from '../ImagePicker/ImagePicker' interface MonthlyInfoProps { onSave: () => Promise<void> onCancel: () => void info: string handleChange: ( value: string, - type: 'info' | 'title' | 'content' | 'question' | 'link' + type: 'info' | 'title' | 'content' | 'question' | 'link' | 'image' ) => void onDelete: (target: ContentItems) => void + imageURL: string } const MonthlyInfo: React.FC<MonthlyInfoProps> = ({ onSave, @@ -20,11 +22,15 @@ const MonthlyInfo: React.FC<MonthlyInfoProps> = ({ info, handleChange, onDelete, + imageURL, }: MonthlyInfoProps) => { return ( <div className="monthlyInfo"> <h2>Informations du mois</h2> <div> + <p className="title">Image</p> + <ImagePicker imageURL={imageURL} handleChange={handleChange} /> + <p className="title">Info</p> <CustomEditor baseState={convertStringToEditorState(info)} handleChange={handleChange} diff --git a/src/models/monthlyInfo.model.ts b/src/models/monthlyInfo.model.ts index 51523e14c276d323cf02acbf7732f53c0a83e94e..b405c4a8ee37e44a6acc088bb7952a9c7f469bb2 100644 --- a/src/models/monthlyInfo.model.ts +++ b/src/models/monthlyInfo.model.ts @@ -2,4 +2,5 @@ export interface IMonthlyInfo { year: number month: number info: string + image: string } diff --git a/src/services/newsletter.service.ts b/src/services/newsletter.service.ts index 2cc2e8f5d634abeba62db6da6c8e68f17265d442..d0f4515aca3fecec6af17b512a6b4ae5a3a24bb6 100644 --- a/src/services/newsletter.service.ts +++ b/src/services/newsletter.service.ts @@ -12,6 +12,7 @@ export class NewsletterService { public saveMonthlyInfo = async ( date: Date, info: string, + image: string, token: string ): Promise<void> => { try { @@ -21,6 +22,7 @@ export class NewsletterService { month: date.getMonth(), year: date.getFullYear(), info: info, + image: image, }, { headers: { @@ -243,4 +245,27 @@ export class NewsletterService { console.error(e) } } + + /** + * Gets the ecogesture images URLs + */ + public getEcogestureImages = async (token: string): Promise<string[]> => { + try { + const { data: imageNames } = await axios.get(`/api/admin/imageNames`, { + headers: { + 'XSRF-TOKEN': token, + }, + }) + if (imageNames && imageNames !== null) { + const imageURLs = imageNames.map((image: string) => { + return `/assets/ecogeste/${image}` + }) + return imageURLs + } + return [] + } catch (e) { + console.error('error', e) + return [] + } + } } diff --git a/src/styles/index.scss b/src/styles/index.scss index 7f768636cde527ced4eb2eba22fbc3bca3eedb64..b8ed46d63473931a98210a9ad2f6f5f76f895873 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -20,3 +20,12 @@ .pagetitle { color: #fafafa; } +.MuiPagination-ul { + justify-content: center; + li button { + color: white; + &.Mui-selected { + color: $gold; + } + } +} diff --git a/template.env b/template.env index 345b85d4d094bbf1207f58bc01ac0ad19bb1d9de..bd65fa3b21069576144c633ac447fbd68eb6690b 100644 --- a/template.env +++ b/template.env @@ -8,6 +8,7 @@ ADMIN_ROLE= DEBUG_MODE= MOCK_OAUTH2= HTTPS_PORT= +IMAGE_FOLDER= # Needed to user OAuth2 authentication : REDIRECT_URL= diff --git a/yarn.lock b/yarn.lock index 471bbe520850f27df814e05385cea2c74a2c8ae3..921c761b6f1db6348adbc08ef207e8e5a17e28fd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -990,6 +990,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.7": + version "7.15.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a" + integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.10.4", "@babel/template@^7.12.13", "@babel/template@^7.3.3": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" @@ -1039,6 +1046,11 @@ version "10.1.0" resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-10.1.0.tgz#f0950bba18819512d42f7197e56c518aa491cf18" +"@emotion/hash@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" + integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== + "@eslint/eslintrc@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.3.0.tgz#d736d6963d7003b6514e6324bec9c602ac340318" @@ -1281,6 +1293,81 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" +"@material-ui/core@^4.12.3": + version "4.12.3" + resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-4.12.3.tgz#80d665caf0f1f034e52355c5450c0e38b099d3ca" + integrity sha512-sdpgI/PL56QVsEJldwEe4FFaFTLUqN+rd7sSZiRCdx2E/C7z5yK0y/khAWVBH24tXwto7I1hCzNWfJGZIYJKnw== + dependencies: + "@babel/runtime" "^7.4.4" + "@material-ui/styles" "^4.11.4" + "@material-ui/system" "^4.12.1" + "@material-ui/types" "5.1.0" + "@material-ui/utils" "^4.11.2" + "@types/react-transition-group" "^4.2.0" + clsx "^1.0.4" + hoist-non-react-statics "^3.3.2" + popper.js "1.16.1-lts" + prop-types "^15.7.2" + react-is "^16.8.0 || ^17.0.0" + react-transition-group "^4.4.0" + +"@material-ui/lab@^4.0.0-alpha.60": + version "4.0.0-alpha.60" + resolved "https://registry.yarnpkg.com/@material-ui/lab/-/lab-4.0.0-alpha.60.tgz#5ad203aed5a8569b0f1753945a21a05efa2234d2" + integrity sha512-fadlYsPJF+0fx2lRuyqAuJj7hAS1tLDdIEEdov5jlrpb5pp4b+mRDUqQTUxi4inRZHS1bEXpU8QWUhO6xX88aA== + dependencies: + "@babel/runtime" "^7.4.4" + "@material-ui/utils" "^4.11.2" + clsx "^1.0.4" + prop-types "^15.7.2" + react-is "^16.8.0 || ^17.0.0" + +"@material-ui/styles@^4.11.4": + version "4.11.4" + resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.11.4.tgz#eb9dfccfcc2d208243d986457dff025497afa00d" + integrity sha512-KNTIZcnj/zprG5LW0Sao7zw+yG3O35pviHzejMdcSGCdWbiO8qzRgOYL8JAxAsWBKOKYwVZxXtHWaB5T2Kvxew== + dependencies: + "@babel/runtime" "^7.4.4" + "@emotion/hash" "^0.8.0" + "@material-ui/types" "5.1.0" + "@material-ui/utils" "^4.11.2" + clsx "^1.0.4" + csstype "^2.5.2" + hoist-non-react-statics "^3.3.2" + jss "^10.5.1" + jss-plugin-camel-case "^10.5.1" + jss-plugin-default-unit "^10.5.1" + jss-plugin-global "^10.5.1" + jss-plugin-nested "^10.5.1" + jss-plugin-props-sort "^10.5.1" + jss-plugin-rule-value-function "^10.5.1" + jss-plugin-vendor-prefixer "^10.5.1" + prop-types "^15.7.2" + +"@material-ui/system@^4.12.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@material-ui/system/-/system-4.12.1.tgz#2dd96c243f8c0a331b2bb6d46efd7771a399707c" + integrity sha512-lUdzs4q9kEXZGhbN7BptyiS1rLNHe6kG9o8Y307HCvF4sQxbCgpL2qi+gUk+yI8a2DNk48gISEQxoxpgph0xIw== + dependencies: + "@babel/runtime" "^7.4.4" + "@material-ui/utils" "^4.11.2" + csstype "^2.5.2" + prop-types "^15.7.2" + +"@material-ui/types@5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@material-ui/types/-/types-5.1.0.tgz#efa1c7a0b0eaa4c7c87ac0390445f0f88b0d88f2" + integrity sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A== + +"@material-ui/utils@^4.11.2": + version "4.11.2" + resolved "https://registry.yarnpkg.com/@material-ui/utils/-/utils-4.11.2.tgz#f1aefa7e7dff2ebcb97d31de51aecab1bb57540a" + integrity sha512-Uul8w38u+PICe2Fg2pDKCaIG7kOyhowZ9vjiC1FsVwPABTW8vPPKfF6OvxRq3IiBaI1faOJmgdvMG7rMJARBhA== + dependencies: + "@babel/runtime" "^7.4.4" + prop-types "^15.7.2" + react-is "^16.8.0 || ^17.0.0" + "@nodelib/fs.scandir@2.1.4": version "2.1.4" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" @@ -1674,6 +1761,13 @@ "@types/history" "*" "@types/react" "*" +"@types/react-transition-group@^4.2.0": + version "4.4.2" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.2.tgz#38890fd9db68bf1f2252b99a942998dc7877c5b3" + integrity sha512-KibDWL6nshuOJ0fu8ll7QnV/LVTo3PzQ9aCPnRUYPfX7eZohHwLIdNHj7pftanREzHNP4/nJa8oeM73uSiavMQ== + dependencies: + "@types/react" "*" + "@types/react@*", "@types/react@^17.0.0": version "17.0.14" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.14.tgz#f0629761ca02945c4e8fea99b8177f4c5c61fb0f" @@ -3061,7 +3155,7 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" -clsx@^1.1.1: +clsx@^1.0.4, clsx@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== @@ -3457,6 +3551,14 @@ css-tree@^1.1.2: mdn-data "2.0.14" source-map "^0.6.1" +css-vendor@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/css-vendor/-/css-vendor-2.0.8.tgz#e47f91d3bd3117d49180a3c935e62e3d9f7f449d" + integrity sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ== + dependencies: + "@babel/runtime" "^7.8.3" + is-in-browser "^1.0.2" + css-what@^3.2.1: version "3.4.2" resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4" @@ -3576,6 +3678,11 @@ cssstyle@^2.2.0: dependencies: cssom "~0.3.6" +csstype@^2.5.2: + version "2.6.18" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.18.tgz#980a8b53085f34af313410af064f2bd241784218" + integrity sha512-RSU6Hyeg14am3Ah4VZEmeX8H7kLwEEirXe6aU2IPfKNvhXwTflK5HQRDNI0ypQXoqmm+QPyG2IaPuQE5zMwSIQ== + csstype@^3.0.2: version "3.0.8" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340" @@ -3810,6 +3917,14 @@ dom-converter@^0.2: dependencies: utila "~0.4" +dom-helpers@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" + integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + dom-serializer@0: version "0.2.2" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" @@ -5197,7 +5312,7 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" -hoist-non-react-statics@^3.1.0: +hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" dependencies: @@ -5358,6 +5473,11 @@ human-signals@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" +hyphenate-style-name@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d" + integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ== + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -5678,6 +5798,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-in-browser@^1.0.2, is-in-browser@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" + integrity sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU= + is-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" @@ -6402,6 +6527,76 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" +jss-plugin-camel-case@^10.5.1: + version "10.7.1" + resolved "https://registry.yarnpkg.com/jss-plugin-camel-case/-/jss-plugin-camel-case-10.7.1.tgz#e7f7097cf97e9deec599cef3275e213452318b93" + integrity sha512-+ioIyWvmAfgDCWXsQcW1NMnLBvRinOVFkSYJUgewQ6TynOcSj5F1bSU23B7z0p1iqK0PPHIU62xY1iNJD33WGA== + dependencies: + "@babel/runtime" "^7.3.1" + hyphenate-style-name "^1.0.3" + jss "10.7.1" + +jss-plugin-default-unit@^10.5.1: + version "10.7.1" + resolved "https://registry.yarnpkg.com/jss-plugin-default-unit/-/jss-plugin-default-unit-10.7.1.tgz#826270e2ee38d7024a281ac67c30d6944f124786" + integrity sha512-tW+dfYVNARBQb/ONzBwd8uyImigyzMiAEDai+AbH5rcHg5h3TtqhAkxx06iuZiT/dZUiFdSKlbe3q9jZGAPIwA== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.7.1" + +jss-plugin-global@^10.5.1: + version "10.7.1" + resolved "https://registry.yarnpkg.com/jss-plugin-global/-/jss-plugin-global-10.7.1.tgz#9725c46d662aac2e596a0a8741944c060e2b90a1" + integrity sha512-FbxCnu44IkK/bw8X3CwZKmcAnJqjAb9LujlAc/aP0bMSdVa3/MugKQRyeQSu00uGL44feJJDoeXXiHOakBr/Zw== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.7.1" + +jss-plugin-nested@^10.5.1: + version "10.7.1" + resolved "https://registry.yarnpkg.com/jss-plugin-nested/-/jss-plugin-nested-10.7.1.tgz#35563a7a710a45307fd6b9742ffada1d72a62eb7" + integrity sha512-RNbICk7FlYKaJyv9tkMl7s6FFfeLA3ubNIFKvPqaWtADK0KUaPsPXVYBkAu4x1ItgsWx67xvReMrkcKA0jSXfA== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.7.1" + tiny-warning "^1.0.2" + +jss-plugin-props-sort@^10.5.1: + version "10.7.1" + resolved "https://registry.yarnpkg.com/jss-plugin-props-sort/-/jss-plugin-props-sort-10.7.1.tgz#1d12b26048541ed3a2ed1b69f7fc231605728362" + integrity sha512-eyd5FhA+J0QrpqXxO7YNF/HMSXXl4pB0EmUdY4vSJI4QG22F59vQ6AHtP6fSwhmBdQ98Qd9gjfO+RMxcE39P1A== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.7.1" + +jss-plugin-rule-value-function@^10.5.1: + version "10.7.1" + resolved "https://registry.yarnpkg.com/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.7.1.tgz#123eb796eb9982f8efa7a7e362daddd90c0c69fe" + integrity sha512-fGAAImlbaHD3fXAHI3ooX6aRESOl5iBt3LjpVjxs9II5u9tzam7pqFUmgTcrip9VpRqYHn8J3gA7kCtm8xKwHg== + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.7.1" + tiny-warning "^1.0.2" + +jss-plugin-vendor-prefixer@^10.5.1: + version "10.7.1" + resolved "https://registry.yarnpkg.com/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.7.1.tgz#217821be2d6dacee31d2d464886760ba7742e19a" + integrity sha512-1UHFmBn7hZNsHXTkLLOL8abRl8vi+D1EVzWD4WmLFj55vawHZfnH1oEz6TUf5Y61XHv0smdHabdXds6BgOXe3A== + dependencies: + "@babel/runtime" "^7.3.1" + css-vendor "^2.0.8" + jss "10.7.1" + +jss@10.7.1, jss@^10.5.1: + version "10.7.1" + resolved "https://registry.yarnpkg.com/jss/-/jss-10.7.1.tgz#16d846e1a22fb42e857b99f9c6a0c5a27341c804" + integrity sha512-5QN8JSVZR6cxpZNeGfzIjqPEP+ZJwJJfZbXmeABNdxiExyO+eJJDy6WDtqTf8SDKnbL5kZllEpAP71E/Lt7PXg== + dependencies: + "@babel/runtime" "^7.3.1" + csstype "^3.0.2" + is-in-browser "^1.1.3" + tiny-warning "^1.0.2" + "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz#41108d2cec408c3453c1bbe8a4aae9e1e2bd8f82" @@ -7564,6 +7759,11 @@ pnp-webpack-plugin@1.6.4: dependencies: ts-pnp "^1.1.6" +popper.js@1.16.1-lts: + version "1.16.1-lts" + resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1-lts.tgz#cf6847b807da3799d80ee3d6d2f90df8a3f50b05" + integrity sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA== + portfinder@^1.0.26: version "1.0.28" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778" @@ -8456,6 +8656,11 @@ react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" +"react-is@^16.8.0 || ^17.0.0": + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + react-is@^17.0.1: version "17.0.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339" @@ -8564,6 +8769,16 @@ react-toastify@^7.0.4: dependencies: clsx "^1.1.1" +react-transition-group@^4.4.0: + version "4.4.2" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470" + integrity sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react@^17.0.2: version "17.0.2" resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" @@ -9856,7 +10071,7 @@ tiny-invariant@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875" -tiny-warning@^1.0.0, tiny-warning@^1.0.3: +tiny-warning@^1.0.0, tiny-warning@^1.0.2, tiny-warning@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"