diff --git a/package-lock.json b/package-lock.json index dff899627684dc4a894a7415e6cd2e0e65709081..895b0bb9210a61f22b4f30d0cd4ac7a5514222c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2038,6 +2038,14 @@ } } }, + "@types/react-recaptcha": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/react-recaptcha/-/react-recaptcha-2.3.3.tgz", + "integrity": "sha512-1IdhJxBbPN+QL/eARSl/qwV2+03kwewJmZ43CWJJoGVZd4S4wGiascCl1HVe8PRtNPzXDqAi+nXIufvEDIsAPg==", + "requires": { + "@types/react": "*" + } + }, "@types/react-transition-group": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz", diff --git a/package.json b/package.json index 6c44b79445f56fdbb0e65b8f636b81ecd597c08e..47016670d92f4ba048442640ddddd3f1b4ca8e07 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@material-ui/lab": "^4.0.0-alpha.57", "@material-ui/styles": "^4.11.2", "@syncfusion/ej2-react-inputs": "^18.3.52", + "@types/react-recaptcha": "^2.3.3", "antd": "^4.13.1", "axios": "^0.21.1", "base64-img": "^1.0.4", diff --git a/public/index.html b/public/index.html index 0cf2c82d7dcdfd0db4cc4afe6f97ccc2492f573c..5a55d5b2604173be763d84d95092a495275df599 100755 --- a/public/index.html +++ b/public/index.html @@ -1,3 +1,3 @@ -<!-- Copyright (C) 2019 Centro de Computacao Cientifica e Software Livre Departamento de Informatica - Universidade Federal do Parana This file is part of Plataforma Integrada MEC. Plataforma Integrada MEC is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Plataforma Integrada MEC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with Plataforma Integrada MEC. If not, see http://www.gnu.org/licenses/. --> <!DOCTYPE html> <html lang="pt-br"> <head> <meta charset="utf-8" /> <meta http-equiv='cache-control' content='no-cache'/> <meta http-equiv='expires' content='0'/> <meta http-equiv='pragma' content='no-cache'/> <!-- google sign-in --> <meta name="google-signin-client_id" content="288460085642-k4veg4fo8kddvjer8b055n9da5qtgha7.apps.googleusercontent.com"> <script src="https://apis.google.com/js/platform.js" async defer></script> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Plataforma integrada MEC RED</title> </head> <body > <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"/> </body> </html> \ No newline at end of file +<!-- Copyright (C) 2019 Centro de Computacao Cientifica e Software Livre Departamento de Informatica - Universidade Federal do Parana This file is part of Plataforma Integrada MEC. Plataforma Integrada MEC is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Plataforma Integrada MEC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with Plataforma Integrada MEC. If not, see http://www.gnu.org/licenses/. --> <!DOCTYPE html> <html lang="pt-br"> <head> <meta charset="utf-8" /> <meta http-equiv='cache-control' content='no-cache'/> <meta http-equiv='expires' content='0'/> <meta http-equiv='pragma' content='no-cache'/> <!-- google sign-in --> <meta name="google-signin-client_id" content="288460085642-k4veg4fo8kddvjer8b055n9da5qtgha7.apps.googleusercontent.com"> <script src="https://apis.google.com/js/platform.js" async defer></script> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Plataforma integrada MEC RED</title> </head> <body > <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"/> <script src="https://www.google.com/recaptcha/api.js" async defer></script> </body> </html> \ No newline at end of file diff --git a/src/App.js b/src/App.js index 512689822628f55d908b129d3fcd267f81f3f22e..18a518916255cebc55e9e55eaaac7c609b909fba 100644 --- a/src/App.js +++ b/src/App.js @@ -52,6 +52,7 @@ import CollectionPage from "./Pages/CollectionPage.js"; import FormationMaterialPage from "./Pages/FormationMaterialPage.js"; import FormationMaterialIframe from "./Pages/FormationMaterialIframe.js"; import MaterialPage from "./Pages/MaterialPage"; +import PageNotFound from "./Pages/PageNotFound.js"; import NoteVariables from "./Admin/Pages/Pages/SubPages/NoteVariables"; import Institution from "./Admin/Pages/Pages/SubPages/Institutions"; @@ -203,6 +204,7 @@ export default function App() { <Route path="/topico" component={FormationMaterialPage} /> <Route path="/iframe-colecao" component={FormationMaterialIframe} /> <Route path="/material-formacao" component={MaterialPage} /> + <Route path='*' component={PageNotFound} /> <div style={{ backgroundColor: " #D3D3D3" }}> <AppBarAdmin /> <div style={{ padding: "2em" }}> diff --git a/src/Components/CollectionCardFunction.js b/src/Components/CollectionCardFunction.js index db76b4b12cb152daa637d6a33949c6dc2b5f6da1..ccfb87838cec4850aa89f3a550ad23dfe2f8223b 100644 --- a/src/Components/CollectionCardFunction.js +++ b/src/Components/CollectionCardFunction.js @@ -22,7 +22,6 @@ import { apiDomain } from '../env'; import noAvatar from "../img/default_profile.png"; import Button from '@material-ui/core/Button'; import styled from 'styled-components' -import Slide from '@material-ui/core/Slide'; import Grid from '@material-ui/core/Grid'; import { StyledCard, CardDiv, CardReaDiv, Footer, LikeCounter, ButtonNoWidth, EnviadoPor, TagContainer, HeaderContainer, AvatarDiv } from './ResourceCardFunction.js' import Rating from '@material-ui/lab/Rating'; @@ -193,39 +192,40 @@ export default function CollectionCardFunction(props) { <StyledCard> <CardDiv> <CardReaDiv> - <Header onMouseEnter={controlSlide} onMouseLeave={controlSlide}> - <Slide direction="left" in={slideIn} timeout={1000}> - <div className={`slideContentLinkAfterActive${slideIn}`} style={{ width: '100%', height: "100%" }}> - <Link to={"/colecao-do-usuario/" + props.id} className="text" style={{ textDecoration: "none" }} > - {SlideAnimationContent()} - </Link> + <Link to={"/colecao-do-usuario/" + props.id}> + <Header onMouseEnter={controlSlide} onMouseLeave={controlSlide}> + <div className={`slideContentLinkBeforeActive${slideIn}`} style={{ width: '272.5px', height: '230px' }}> + <UserInfo style={{ width: '272.5px'}}> + {/* I(Luis) dont know why, but if i use styled components, sometimes the avatar will be deconfigured */} + <img src={userAvatar} alt="user avatar" style={{ + height: "70px", width: "70px", borderRadius: "50%", + zIndex: 1, border: "2px solid white", + boxShadow: "0 1px 3px rgba(0,0,0,.45)" + }} /> + <UserAndTitle> + <span>{props.author}</span> + <span className={"col-name"}>{name}</span> + </UserAndTitle> + </UserInfo> + <StyledGrid container direction="row" style={{ width: '272.5px'}}> + { + props.thumbnails.map((thumb) => + <Grid item xs={props.thumbnails <= 4 && props.thumbnails > 0 ? 12 / props.thumbnails.length : 6}> + <div style={{ backgroundImage: `url(${`${apiDomain}` + thumb})`, height: "100%", width: "100%", backgroundSize: "cover", backgroundPosition: "center" }} /> + </Grid> + ) + } + </StyledGrid> </div> - </Slide> - <div className={`slideContentLinkBeforeActive${slideIn}`} style={{ width: '100%', height: '100%' }}> - <UserInfo> - {/* I(Luis) dont know why, but if i use styled components, sometimes the avatar will be deconfigured */} - <img src={userAvatar} alt="user avatar" style={{ - height: "70px", width: "70px", borderRadius: "50%", - zIndex: 1, border: "2px solid white", - boxShadow: "0 1px 3px rgba(0,0,0,.45)" - - }} /> - <UserAndTitle> - <span>{props.author}</span> - <span className={"col-name"}>{name}</span> - </UserAndTitle> - </UserInfo> - <StyledGrid container direction="row"> - { - props.thumbnails.map((thumb) => - <Grid item xs={props.thumbnails <= 4 && props.thumbnails > 0 ? 12 / props.thumbnails.length : 6}> - <div style={{ backgroundImage: `url(${`${apiDomain}` + thumb})`, height: "100%", width: "100%", backgroundSize: "cover", backgroundPosition: "center" }} /> - </Grid> - ) - } - </StyledGrid> - </div> - </Header> + { + <div className={`slideContentLinkAfterActive${slideIn}`} style={{ width: '272.5px', height: '230px' }}> + <div className="text" > + {SlideAnimationContent()} + </div> + </div> + } + </Header> + </Link> <Description> {/*renders rating, number of learning objects and likes count*/} { @@ -319,8 +319,8 @@ export default function CollectionCardFunction(props) { const SlideContentDiv = styled.div` background-color : #7e57c2; padding : 10px; - width : 100%; - height : 100%; + width : 272.5px; + height : 230px; ` const UserAndTitle = styled.div` diff --git a/src/Components/MenuBar.js b/src/Components/MenuBar.js index 7cf1153b7bdf834d4694b1d7367df27fbc9319ca..cd5cb4d2e7dbe4f53282798b0a4f921b58933d32 100644 --- a/src/Components/MenuBar.js +++ b/src/Components/MenuBar.js @@ -175,8 +175,7 @@ export default function MenuBar(props) { <CloudUploadIcon style={{ color: "white", marginLeft: "0" }} /> <span style={{ color: "#fff", textAlign: "center", alignSelf: "center", fontWeight: "500" }} > PUBLICAR RECURSO - </span> - + </span> </ButtonPublicarRecurso> </Link> </div> diff --git a/src/Components/MobileDrawerMenu.js b/src/Components/MobileDrawerMenu.js index 654ee7b9154f7fd34b15c8b36ee0c163e301f45f..91992ef27c79b10e8279837aafbdf7f70ee6e43a 100644 --- a/src/Components/MobileDrawerMenu.js +++ b/src/Components/MobileDrawerMenu.js @@ -33,6 +33,7 @@ import DefaultAvatar from '../img/default_profile0.png' import SettingsIcon from '@material-ui/icons/Settings'; import { apiDomain } from '../env.js' import { deleteRequest } from './HelperFunctions/getAxiosConfig' +import CloudUploadIcon from '@material-ui/icons/CloudUpload'; export default function MobileDrawerMenu(props) { const { state, dispatch } = useContext(Store) @@ -126,33 +127,43 @@ export default function MobileDrawerMenu(props) { { /*If user is logged in, render user actions menu; else render log in/sign in buttons*/ state.userIsLoggedIn ? - ( - <div style={{ display: "flex", flexDirection: "column", color: "#666", paddingBottom: "10px" }}> - <MyArea> - <div className="user-avatar"> - <img alt="user-avatar" - src={getUserAvatar()} - /> - </div> - <span className="text">Minha área</span> - </MyArea> - - { - minhaArea.map((item, index) => - <Link to={{ - pathname: item.href, - state: item.value - }} - className={`menu-buttons ${selectedIndex === (index + menuSobre.length) ? 'selected' : ''}`} - onClick={(event) => handleMenuItemClick(event, index + menuSobre.length)} - > - {item.icon} - {item.name} - </Link> - ) - } - - </div> + ( + <> + <div style={{ display: "flex", justifyContent: "left", marginTop: "10px", marginBottom: "10px" }}> + <Link to="/termos-publicar-recurso"> + <ButtonPublicarRecurso> + <CloudUploadIcon style={{ color: "white", marginRight: "10px" }} /> + <span style={{ color: "white", textAlign: "center", alignSelf: "center", fontWeight: "500" }} > + PUBLICAR RECURSO + </span> + </ButtonPublicarRecurso> + </Link> + </div> + <div style={{ display: "flex", flexDirection: "column", color: "#666", paddingBottom: "10px" }}> + <MyArea> + <div className="user-avatar"> + <img alt="user-avatar" + src={getUserAvatar()} + /> + </div> + <span className="text">Minha área</span> + </MyArea> + { + minhaArea.map((item, index) => + <Link to={{ + pathname: item.href, + state: item.value + }} + className={`menu-buttons ${selectedIndex === (index + menuSobre.length) ? 'selected' : ''}`} + onClick={(event) => handleMenuItemClick(event, index + menuSobre.length)} + > + {item.icon} + {item.name} + </Link> + ) + } + </div> + </> ) : ( @@ -160,7 +171,7 @@ export default function MobileDrawerMenu(props) { <div style={{ display: "flex", justifyContent: "center", marginTop: "10px" }}> <ButtonPublicarRecurso onClick={props.openLogin}> PUBLICAR RECURSO? - </ButtonPublicarRecurso> + </ButtonPublicarRecurso> </div> <div style={{ display: "flex", flexDirection: "row", margin: "10px 0", justifyContent: "center" }}> @@ -228,6 +239,7 @@ const MyArea = styled.div` const ButtonPublicarRecurso = styled(Button)` font-weight : 500 !important; border : 1.5px #666 solid !important; + background-color : #ff7f00 !important; color: #666; box-shadow: none; margin : 0 8px !important; diff --git a/src/Components/PublicationPermissionsContent.js b/src/Components/PublicationPermissionsContent.js index 1159d667ac2d542368d96b490db2cc10441f3156..2f404e49217f7c9c83541ed84c3852ce94d06b65 100644 --- a/src/Components/PublicationPermissionsContent.js +++ b/src/Components/PublicationPermissionsContent.js @@ -72,7 +72,7 @@ export default function PublicationPermissionsContent (props) { <p>{question.description}</p> </Grid> <Grid item xs={2}> - <RadioGroup row name={"radio" + (index + 1)} onChange={props.handleRadios}> + <RadioGroup row name={"radio" + (question.id)} onChange={props.handleRadios}> <FormControlLabel value="Sim" control={<BlueRadio/>} label="Sim"/> <FormControlLabel value="Não" control={<BlueRadio/>} label="Não"/> </RadioGroup> </Grid> diff --git a/src/Components/ResourceCard.css b/src/Components/ResourceCard.css index c7b97b5cb76d3373765410177a1cfd4749a31fef..345a97b61a931e5b09c711afb69a4c192cb4b7d6 100644 --- a/src/Components/ResourceCard.css +++ b/src/Components/ResourceCard.css @@ -1,22 +1,26 @@ /* transform: translateX(-1284.61px); visibility: hidden; */ -/* .slideContentLinkAfterActivefalse{ - transform: translateX(-1285.55px); +.slideContentLinkAfterActivefalse{ position: relative; -}*/ -.slideContentLinkAfterActivetrue{ - position: absolute; transform: none; + transition: cubic-bezier(1, 1, 0, 0) 750ms; +} + +.slideContentLinkAfterActivetrue{ + transform: translateX(-272.5px); + transition: transform 750ms; + position: relative; /* transition: transform 1000ms cubic-bezier(0,0,0.2,1) 0ms; */ } .slideContentLinkBeforeActivefalse{ - position: absolute; + position: relative; transform: none; - transition: transform 500ms cubic-bezier(0,0,0.2,1) 0ms; + transition: cubic-bezier(0, 0, 1, 1) 750ms; } .slideContentLinkBeforeActivetrue{ - transform: translateX(-1285.55px); + transform: translateX(-272.5px); + transition: transform 750ms; position: relative; } \ No newline at end of file diff --git a/src/Components/ResourceCardFunction.js b/src/Components/ResourceCardFunction.js index d688ca3e6e154871fc8633416529f651e531e530..62aa64614e3427ad4f9617f1ac6ce3daf956ce28 100644 --- a/src/Components/ResourceCardFunction.js +++ b/src/Components/ResourceCardFunction.js @@ -27,13 +27,16 @@ import Rating from '@material-ui/lab/Rating'; import StarBorderIcon from '@material-ui/icons/StarBorder'; import FavoriteIcon from '@material-ui/icons/Favorite'; import ButtonGuardarColecao from './ButtonGuardarColecao.js' -import Slide from '@material-ui/core/Slide'; import Grid from '@material-ui/core/Grid'; import { Link } from 'react-router-dom'; import { getDefaultThumbnail } from './HelperFunctions/getDefaultThumbnail' import GetIconByName from './UploadPageComponents/GetIconByName' import "./ResourceCard.css"; import { putRequest } from './HelperFunctions/getAxiosConfig' +import SignUpModal from './SignUpModal' +import LoginModal from './LoginModal.js' +import Snackbar from '@material-ui/core/Snackbar'; +import MuiAlert from '@material-ui/lab/Alert'; export default function ResourceCardFunction(props) { const [thumbnail, setThumbnail] = useState(null) @@ -45,6 +48,14 @@ export default function ResourceCardFunction(props) { const [liked, toggleLiked] = useState(props.liked) const [likesCount, setLikesCount] = useState(props.likeCount) + const [signUpOpen, setSignUp] = useState(false) + const [loginOpen, setLogin] = useState(false) + const [successfulLoginOpen, handleSuccessfulLogin] = useState(false) + + function Alert(props) { + return <MuiAlert elevation={6} variant="filled" {...props} />; + } + useEffect(() => { //decide which thumbnail to use if (props.thumbnail) { @@ -74,6 +85,21 @@ export default function ResourceCardFunction(props) { putRequest(url, {}, handleSuccessLike, (error) => { console.log(error) }) } + const handleLogin = () => { + setLogin(!loginOpen) + } + + const handleSignUp = () => { + setSignUp(!signUpOpen) + } + + function toggleLoginSnackbar(reason) { + if (reason === 'clickaway') { + return; + } + handleSuccessfulLogin(false); + } + const SlideAnimationContent = () => { return ( <SlideContentDiv> @@ -106,64 +132,77 @@ export default function ResourceCardFunction(props) { } return ( - <StyledCard> - <CardDiv> - <CardReaDiv> - <Header onMouseEnter={controlSlide} onMouseLeave={controlSlide}> - { - <Slide direction="left" in={slideIn} timeout={1000}> - <div className={`slideContentLinkAfterActive${slideIn}`}> - <Link to={props.href} className="text" > - {SlideAnimationContent()} - </Link> + <React.Fragment> + <SignUpModal open={signUpOpen} handleClose={handleSignUp} openLogin={handleLogin} + /> + <LoginModal open={loginOpen} handleClose={() => setLogin(false)} openSignUp={handleSignUp} + openSnackbar={() => { handleSuccessfulLogin(true) }} + /> + <Snackbar open={successfulLoginOpen} autoHideDuration={1000} onClose={toggleLoginSnackbar} + anchorOrigin={{ vertical: 'top', horizontal: 'center' }} + > + <Alert severity="success" style={{ backgroundColor: "#00acc1" }}>Você está conectado(a)!</Alert> + </Snackbar> + <StyledCard> + <CardDiv> + <CardReaDiv> + <Link to={props.href}> + <Header onMouseEnter={controlSlide} onMouseLeave={controlSlide}> + <div className={`slideContentLinkBeforeActive${slideIn}`} style={{ height: '189px' }}> + <img className="img-cover" src={thumbnail} alt="learning object thumbnail" style={{ width: "272.5px" }} /> </div> - </Slide > - } - <div className={`slideContentLinkBeforeActive${slideIn}`} style={{ height: '100%' }}> - <img className="img-cover" src={thumbnail} alt="learning object thumbnail" style={{ width: "272.5px" }} /> - </div> - </Header> - <Description> - <Link to={props.href} className="text" style={{ height: '45px' }}> {/*add link to learningObject*/} - <Title> - {props.title} - </Title> + { + <div className={`slideContentLinkAfterActive${slideIn}`}> + <div className="text" > + {SlideAnimationContent()} + </div> + </div> + } + </Header> </Link> - <Rating - name="customized-empty" - value={props.rating} - readOnly - style={{ color: "#666" }} - emptyIcon={<StarBorderIcon fontSize="inherit" />} - /> - <Footer> - <Type> - {GetIconByName(label)} - <span>{label}</span> - </Type> - <LikeCounter> - <span>{likesCount}</span> - <ButtonNoWidth onClick={handleLike}> - <FavoriteIcon style={{ color: liked ? "red" : "#666" }} /> - </ButtonNoWidth> - </LikeCounter> - </Footer> - </Description> - </CardReaDiv> - <CardReaFooter> - <div style={{ display: "flex", height: "100%" }}> - <ButtonGuardarColecao thumb={props.thumbnail} title={props.title} learningObjectId={props.id} + <Description> + <Link to={props.href} className="text" style={{ height: '45px' }}> {/*add link to learningObject*/} + <Title> + {props.title} + </Title> + </Link> + <Rating + name="customized-empty" + value={props.rating} + readOnly + style={{ color: "#666" }} + emptyIcon={<StarBorderIcon fontSize="inherit" />} + /> + <Footer> + <Type> + {GetIconByName(label)} + <span>{label}</span> + </Type> + <LikeCounter> + <span>{likesCount}</span> + <ButtonNoWidth onClick={handleLike}> + <FavoriteIcon style={{ color: liked ? "red" : "#666" }} /> + </ButtonNoWidth> + </LikeCounter> + </Footer> + </Description> + </CardReaDiv> + <CardReaFooter> + <div style={{ display: "flex", height: "100%" }}> + <ButtonGuardarColecao thumb={props.thumbnail} title={props.title} learningObjectId={props.id} + /> + </div> + <ResourceCardOptions + learningObjectId={props.id} + downloadableLink={props.downloadableLink} + thumb={props.thumbnail} + title={props.title} + handleLogin={handleLogin} /> - </div> - <ResourceCardOptions - learningObjectId={props.id} - downloadableLink={props.downloadableLink} - thumb={props.thumbnail} - title={props.title} - /> - </CardReaFooter> - </CardDiv> - </StyledCard> + </CardReaFooter> + </CardDiv> + </StyledCard> + </React.Fragment> ) } /*---------- USED IN SLIDE DIV ONLY -----------*/ diff --git a/src/Components/ResourceCardOptions.js b/src/Components/ResourceCardOptions.js index 5d2821f3d9a39cc92af23d5ff1fa7c71de868777..2e89a35a708663188a9e843c3fbb08c8de6deaff 100644 --- a/src/Components/ResourceCardOptions.js +++ b/src/Components/ResourceCardOptions.js @@ -36,10 +36,6 @@ import ShareModal from './ShareModal' import SnackbarComponent from './SnackbarComponent' import OpenInBrowserIcon from '@material-ui/icons/OpenInBrowser'; import Tooltip from '@material-ui/core/Tooltip'; -import SignUpModal from './SignUpModal' -import LoginModal from './LoginModal.js' -import Snackbar from '@material-ui/core/Snackbar'; -import MuiAlert from '@material-ui/lab/Alert'; import { getRequest } from './HelperFunctions/getAxiosConfig' export default function ResourceCardOptions(props) { @@ -68,7 +64,7 @@ export default function ResourceCardOptions(props) { const [saveToCol, toggleSave] = useState(false) const handleGuardar = () => { if (!state.currentUser.id) { - handleLogin(); + props.handleLogin(); } else { toggleSave(true); @@ -93,7 +89,7 @@ export default function ResourceCardOptions(props) { const handleShare = () => { if (!state.currentUser.id) { - handleLogin() + props.handleLogin() } else { toggleShare(true); @@ -104,17 +100,9 @@ export default function ResourceCardOptions(props) { return (window.origin + "/recurso/" + props.learningObjectId) } - const handleSignUp = () => { - setSignUp(!signUpOpen) - } - - const handleLogin = () => { - setLogin(!loginOpen) - } - const handleReport = () => { if (!state.currentUser.id) { - handleLogin() + props.handleLogin() } else { handleModalReportar(true); @@ -122,21 +110,7 @@ export default function ResourceCardOptions(props) { handleClose(); } - function Alert(props) { - return <MuiAlert elevation={6} variant="filled" {...props} />; - } - - function toggleLoginSnackbar(reason) { - if (reason === 'clickaway') { - return; - } - handleSuccessfulLogin(false); - } - const [snackbarOpen, toggleSnackbar] = useState(false) - const [signUpOpen, setSignUp] = useState(false) - const [loginOpen, setLogin] = useState(false) - const [successfulLoginOpen, handleSuccessfulLogin] = useState(false) return ( <> @@ -155,16 +129,6 @@ export default function ResourceCardOptions(props) { /> <SnackbarComponent snackbarOpen={snackbarOpen} severity={"info"} handleClose={() => { toggleSnackbar(false) }} text={"Baixando o Recurso... Lembre-se de relatar sua experiência após o uso do Recurso!"} /> - <SignUpModal open={signUpOpen} handleClose={handleSignUp} openLogin={handleLogin} - /> - <LoginModal open={loginOpen} handleClose={() => setLogin(false)} openSignUp={handleSignUp} - openSnackbar={() => { handleSuccessfulLogin(true) }} - /> - <Snackbar open={successfulLoginOpen} autoHideDuration={1000} onClose={toggleLoginSnackbar} - anchorOrigin={{ vertical: 'top', horizontal: 'center' }} - > - <Alert severity="success" style={{ backgroundColor: "#00acc1" }}>Você está conectado(a)!</Alert> - </Snackbar> <div style={{ fontSize: "12px", display: "flex", flexDirection: "column", justifyContent: "center" }}> <ButtonNoWidth aria-controls="simple-menu" aria-haspopup="true" onClick={handleClick} style={{ color: "#666" }}> <MoreVertIcon style={{ color: "#666" }} /> diff --git a/src/Components/ResourcePageComponents/TextoObjeto.js b/src/Components/ResourcePageComponents/TextoObjeto.js index 1554cc9ebd70387fd72cb155f749232133f1b5c0..f19b82e1d1c55de62c7884e36c63827c7fe77b70 100644 --- a/src/Components/ResourcePageComponents/TextoObjeto.js +++ b/src/Components/ResourcePageComponents/TextoObjeto.js @@ -104,7 +104,12 @@ export default function TextoObjeto (props) { return ( <TextoObjetoDiv> { publisherDeletedObject && - <Redirect to="/"/> + <Redirect + to={{ + pathname: "/perfil", + state: 1 + }} + /> } <Snackbar open={snackbarOpen} autoHideDuration={1000} onClose={() => {toggleSnackbar(false)}} anchorOrigin = {{ vertical:'top', horizontal:'right' }} diff --git a/src/Components/SignUpContainerFunction.js b/src/Components/SignUpContainerFunction.js index 89343fe5ea8d726c1cdb503b68cb280e82b10c46..89752091cd4787555592c3c307e0ea0424395073 100644 --- a/src/Components/SignUpContainerFunction.js +++ b/src/Components/SignUpContainerFunction.js @@ -28,6 +28,7 @@ import {apiUrl} from '../env.js' import {GoogleLoginButton} from './LoginContainerFunction' import ValidateUserInput from './HelperFunctions/FormValidationFunction.js' import GoogleLogo from "../img/logo_google.svg" +import ReCaptcha from 'react-recaptcha' async function handleGoogleAttempt () { console.log("handleGoogleAttempt") @@ -37,6 +38,8 @@ async function handleGoogleAttempt () { window.location.replace(request_url) } export default function SignUpContainer (props) { + const [unavailableButton, setButtonAvailability] = useState(true); + const [formNome, setNome] = useState( { key : false, @@ -137,6 +140,12 @@ export default function SignUpContainer (props) { } } + function captchaVerified (response) { + if (response) { + setButtonAvailability(false) + } + } + return ( <ContainerStyled > <DialogHeaderStyled> @@ -208,8 +217,17 @@ export default function SignUpContainer (props) { help = {formConfirmation.key ? (formConfirmation.value.length === 0 ? "Faltou digitar sua senha." : (formConfirmation.value !== formSenha.value ? "As senhas precisam ser iguais" : "A senha precisa ter no mínimo 8 caracteres.")) : ""} /> <br/> + <div style={{margin:"0 auto", width: "304px"}}> + { + //<ReCaptcha sitekey={process.env.REACT_APP_SITE_KEY} verifyCallback={captchaVerified} /> //when key set in env + //<ReCaptcha sitekey="6LfxuKUUAAAAAIzYpCzEtJyeE8QRjBYa44dvHlTX" verifyCallback={captchaVerified} /> //use this one on production + <ReCaptcha sitekey="6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI" verifyCallback={captchaVerified}/> //test key, from google, do not use this one on production + } + </div> <ConfirmContainerStyled> - <StyledSignUpButton type="submit" variant="contained"> + <StyledSignUpButton type="submit" variant="contained" disabled={unavailableButton} + style={unavailableButton ? { backgroundColor: "#e9e9e9" } : { backgroundColor: "#00bcd4" }} + > <span style={{paddingLeft:"16px", paddingRight:"16px", borderRadius:"3px", boxSizing:"border-box", fontFamily:"Roboto, sans serif", fontWeight:"500", color:"#fff"}} @@ -335,7 +353,6 @@ const ConfirmContainerStyled = styled.div` ` const StyledSignUpButton = styled(Button)` - background-color: #00bcd4 !important; box-shadow : none !important; outline: none !important; border : 0 !important; diff --git a/src/Components/TabPanels/UserPageTabs/PanelEditarPerfil.js b/src/Components/TabPanels/UserPageTabs/PanelEditarPerfil.js index ce5a319efaacbf631c7a3871c0ee8672a8f68db1..f65e6417a460b08bf87221fb28aa8cec321a2c97 100644 --- a/src/Components/TabPanels/UserPageTabs/PanelEditarPerfil.js +++ b/src/Components/TabPanels/UserPageTabs/PanelEditarPerfil.js @@ -42,7 +42,7 @@ export default function TabPanelEditarPerfil(props) { }) const [formAboutMe, setAboutMe] = useState({ - key: state.currentUser.description ? false : true, + key: false, value: state.currentUser.description ? state.currentUser.description : "" }) diff --git a/src/Components/UploadPageComponents/Forms/Idioma.js b/src/Components/UploadPageComponents/Forms/Idioma.js index 7d033a44d9ef18c7ffdd55f5b7894176077d3e7d..79de2d04f8be2e66ca72f772679999af94e06b78 100644 --- a/src/Components/UploadPageComponents/Forms/Idioma.js +++ b/src/Components/UploadPageComponents/Forms/Idioma.js @@ -26,38 +26,46 @@ import Select from '@material-ui/core/Select'; export default function Idioma (props) { const [chosenLanguage, setChosenLanguage] = useState(props.initialValue ? props.initialValue : []) - const [ids, setIds] = useState(props.initialIDValues ? props.initialIDValues : []) + const [ids, setIds] = useState(props.initialIDValue ? props.initialIDValue : []) const handleChangeLanguage = (event) => { - console.log(event.target.value) let newLanguage = event.target.value.pop() - setChosenLanguage(chosenLanguage => [...chosenLanguage, newLanguage.name]); - setIds(ids => [...ids, newLanguage.id]) - }; + if (!chosenLanguage.some(language => language === newLanguage.name)) { + setChosenLanguage(chosenLanguage => [...chosenLanguage, newLanguage.name]); + setIds(ids => [...ids, newLanguage.id]) + } + else + { + if (chosenLanguage.length > 0) { + setChosenLanguage(chosenLanguage.filter((language) => {return language !== newLanguage.name})); + setIds(ids.filter((id) => {return id !== newLanguage.id})) + } + } + }; return ( - <FormControl required style={{minWidth : "30%"}}> + <FormControl style={{minWidth : "30%"}}> <StyledFormLabel component="legend" style={{fontSize : "14px", marginBottom : "10px"}}> - <b>Idioma</b> + <b>Idioma</b><span>*</span> </StyledFormLabel> - <Select - value={chosenLanguage} - multiple - renderValue={(selected) => selected.join(', ')} - name="Idioma" - onChange={handleChangeLanguage} - onBlur={() => {props.onBlurCallback("language_ids", ids, props.draftID)}} - > - { - props.languages.map( language => - <MenuItem key={language.name} value={language}> - <Checkbox checked={chosenLanguage.indexOf(language.name) > -1} /> - <ListItemText primary={language.name} /> - </MenuItem> - ) - } - </Select> + <Select + value={chosenLanguage} + multiple + renderValue={(selected) => selected.join(', ')} + name="Idioma" + onChange={handleChangeLanguage} + onBlur={() => {props.onBlurCallback("language_ids", ids, props.draftID)}} + > + { + props.languages.map( language => + <MenuItem key={language.name} value={language}> + <Checkbox checked={chosenLanguage.indexOf(language.name) > -1} /> + <ListItemText primary={language.name} /> + </MenuItem> + ) + } + </Select> </FormControl> ) } diff --git a/src/Components/UploadPageComponents/Forms/Keywords.js b/src/Components/UploadPageComponents/Forms/Keywords.js index 34c7b8ec36103d4eb27f1fbba515f2cd686ffdad..fa2418b706dc96f56f88f60f7ef1014fd66b40dd 100644 --- a/src/Components/UploadPageComponents/Forms/Keywords.js +++ b/src/Components/UploadPageComponents/Forms/Keywords.js @@ -16,7 +16,7 @@ GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with Plataforma Integrada MEC. If not, see <http://www.gnu.org/licenses/>.*/ -import React, {useState, memo} from 'react' +import React, {useState, memo, useEffect, useRef} from 'react' import FormControl from '@material-ui/core/FormControl'; import {StyledTextField, StyledFormLabel} from '../StyledComponents.js' import FormHelperText from '@material-ui/core/FormHelperText'; @@ -27,9 +27,18 @@ function Keywords (props) { const [keywords, setKeywords] = useState(props.initialValue ? props.initialValue : []) const handleSetKeywords = (newKeyword) => {setKeywords(newKeyword)} const deleteKeyword = (keywordToDelete) => { - setKeywords(keywords.filter((keyword) => keyword !== keywordToDelete)) + handleSetKeywords(keywords.filter((keyword) => keyword !== keywordToDelete)) } + const resettingRef = useRef(false); + + useEffect(() => { + if(resettingRef.current){ //used to ensure that keywords are updated before sending (after a delete) + resettingRef.current = false; + props.onBlurCallback("tags", keywords, props.draftID); + } + },[keywords]) + const [keywordsBuffer, setKeywordsBuffer] = useState('') const handleKeywords = (event) => { @@ -64,8 +73,9 @@ function Keywords (props) { onKeyDown={(event) => { if(event.keyCode === 13){ handleSetKeywords([...keywords, keywordsBuffer]) - setKeywordsBuffer('')}} - } + setKeywordsBuffer('') + }} + } fullWidth onBlur={() => {props.onBlurCallback("tags", keywords, props.draftID)}} /> @@ -75,7 +85,7 @@ function Keywords (props) { <FormHelperText> { keywords.map( (keyword) => - <Chip label={keyword} key={keyword} onDelete={() => deleteKeyword(keyword)} /> + <Chip label={keyword} key={keyword} onDelete={() => {resettingRef.current = true; deleteKeyword(keyword);}} /> ) } </FormHelperText> diff --git a/src/Components/UploadPageComponents/PartOne.js b/src/Components/UploadPageComponents/PartOne.js index fbb47cb5b0e325fa01cf5ba7666acd454a398134..250428dbf319dd5488a5bcebb31743d787d6a73b 100644 --- a/src/Components/UploadPageComponents/PartOne.js +++ b/src/Components/UploadPageComponents/PartOne.js @@ -16,7 +16,8 @@ GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with Plataforma Integrada MEC. If not, see <http://www.gnu.org/licenses/>.*/ -import React, {useState, useEffect} from 'react' +import React, {useState, useEffect, useContext, useRef} from 'react' +import {Store} from '../../Store.js' import Grid from '@material-ui/core/Grid'; import ButtonsDiv from './ButtonsDiv.js' import SobreORecurso from './Forms/SobreORecurso.js' @@ -27,67 +28,194 @@ import TipoDeRecurso from './Forms/TipoDeRecurso.js' import Idioma from './Forms/Idioma.js' import {SendInfo} from './SendInfo.js' import {getRequest} from '../HelperFunctions/getAxiosConfig.js' +import SnackBar from '../../Components/SnackbarComponent'; +import LoadingSpinner from '../../Components/LoadingSpinner' export default function PartOne (props) { // {/*const [subjects, setSubjects] = useState([])*/} + const {state} = useContext(Store) + const didMountRef = useRef(false); + const didMountRefObj = useRef(false); + const [languages, setLanguages] = useState([]) const [objTypes, setObjTypes] = useState([]) + const [learningObject, setLearningObject] = useState({}) + + const [loading, toggleLoading] = useState(true) + const [loadingObj, toggleLoadingObj] = useState(true) + + const [snackInfo, setSnackInfo] = useState({ + open: false, + text: '', + severity: '', + color: '', + }) + + function handleCloseSnackBar() { + const info = { + open: false, + text: '', + severity: '', + color: '', + } + handleSnackInfo(info) + } + + function handleSnackInfo(info) { + setSnackInfo({ + ...info + }) + } + + function checkPartOne (data) { + return ( + data.name !== null && + data.tags.length !== 0 && + data.author !== null && + data.object_type !== null && + data.language.length !== 0 && + (data.attachments.length !== 0 || + data.link !== null) + ) + } + function handleSuccessGetObjTypes (data) { setObjTypes(data.sort((a, b) => (a.name) > (b.name) ? 1 : -1)) } + + function handleSuccessGetFormData (data) { + if (checkPartOne(data)) { + props.stepperControl(1) + } else { + const info = { + open: true, + text: 'Preencha todos os campos obrigatórios, inclusive o de "Enviar Recurso"!', + severity: 'warning', + color: '#FFC125', + } + handleSnackInfo(info) + } + } + + function handleSuccessfulGet (data) { + setLearningObject(data) + } + useEffect( () => { - getRequest(`/object_types/`, handleSuccessGetObjTypes, (error) => {console.log(error)}) + getRequest(`/object_types/`, handleSuccessGetObjTypes, (error) => {console.log(error)}) + getRequest(`/languages/`, (data) => {setLanguages(data)}, (error) => {console.log(error)}) - getRequest(`/languages/`, (data) => {setLanguages(data)}, (error) => {console.log(error)}) + const url = `/learning_objects/${props.draftID}` + getRequest(url, handleSuccessfulGet, (error) => {console.log(error)}) }, []) - const handleSubmit = () => { - props.stepperControl(1) + useEffect( () => { + if (didMountRef.current) { + toggleLoading(false) + } + else { + didMountRef.current = true; + } + }, [learningObject]) + + useEffect( () => { + if (didMountRefObj.current) { + toggleLoadingObj(false) + } + else { + didMountRefObj.current = true; + } + }, [objTypes]) + + const handleSubmit = (e) => { + e.preventDefault(); + getRequest(`/learning_objects/${props.draftID}`, + handleSuccessGetFormData, + () => { + const info = { + open: true, + text: 'Não foi possível verificar o status da publicação!', + severity: 'error', + color: 'red', + } + handleSnackInfo(info) + } + ) } return ( - <form onSubmit={handleSubmit}> - {/*------------------------------Titulo-----------------------------------------*/} - <Grid item xs={12} style={{paddingBottom : "40px"}}> - <NewTitle draftID={props.draftID} onBlurCallback={SendInfo}/> - </Grid> - - {/*------------------------------Sobre------------------------------------------*/} - <Grid item xs={12} style={{paddingBottom : "40px"}}> - <SobreORecurso draftID={props.draftID} onBlurCallback={SendInfo}/> - </Grid> - - {/*------------------------------Palavras-chave------------------------------------------*/} - <Grid item xs={12} style={{paddingBottom : "40px"}}> - <Keywords draftID={props.draftID} onBlurCallback={SendInfo}/> - </Grid> - - {/*------------------------------Autor------------------------------------------*/} - <Grid item xs={12} style={{paddingBottom : "40px"}}> - <Autor draftID={props.draftID} onBlurCallback={SendInfo}/> - </Grid> - - {/*------------------------------Tipo do Objeto------------------------------------------*/} - <Grid item xs={12} style={{paddingBottom : "40px"}}> - <TipoDeRecurso objTypes={objTypes} draftID={props.draftID} onBlurCallback={SendInfo}/> - </Grid> - - {/*------------------------------Idioma------------------------------------------*/} - <Grid item xs={12} style={{paddingBottom : "40px"}}> - <Idioma languages={languages} draftID={props.draftID} onBlurCallback={SendInfo}/> - </Grid> - - {/*------------------------------Botoes------------------------------------------*/} - <Grid item xs={12}> - <ButtonsDiv draftID={props.draftID} stepperControl={props.stepperControl}/> - </Grid> - - <Grid item xs={12} style={{marginTop : "20px"}}> - <span style={{marginTop : "20px", fontWeight : "200", color : "#a5a5a5", paddingLeft : "10px"}}> - * Campos obrigatórios - </span> - </Grid> - </form> + <React.Fragment> + <SnackBar + snackbarOpen={snackInfo.open} + handleClose={handleCloseSnackBar} + severity={snackInfo.severity} + color={snackInfo.color} + text={snackInfo.text} + /> + { + !loading && !loadingObj ? ( + <form onSubmit={handleSubmit}> + {/*------------------------------Titulo-----------------------------------------*/} + <Grid item xs={12} style={{paddingBottom : "40px"}}> + <NewTitle draftID={props.draftID} onBlurCallback={SendInfo} initialValue={learningObject.name}/> + </Grid> + + {/*------------------------------Sobre------------------------------------------*/} + <Grid item xs={12} style={{paddingBottom : "40px"}}> + <SobreORecurso draftID={props.draftID} onBlurCallback={SendInfo} initialValue={learningObject.description}/> + </Grid> + + {/*------------------------------Palavras-chave------------------------------------------*/} + <Grid item xs={12} style={{paddingBottom : "40px"}}> + <Keywords draftID={props.draftID} onBlurCallback={SendInfo} initialValue={learningObject.tags !== undefined ? learningObject.tags.map((tag) => tag.name) : null}/> + </Grid> + + {/*------------------------------Autor------------------------------------------*/} + <Grid item xs={12} style={{paddingBottom : "40px"}}> + <Autor draftID={props.draftID} onBlurCallback={SendInfo} + initialValue={ + learningObject.author === state.currentUser.name ? + 0 : 1 + } + initialOutroAutor={ + learningObject.author !== state.currentUser.name ? + learningObject.author : '' + } + /> + </Grid> + + {/*------------------------------Tipo do Objeto------------------------------------------*/} + <Grid item xs={12} style={{paddingBottom : "40px"}}> + <TipoDeRecurso objTypes={objTypes} draftID={props.draftID} onBlurCallback={SendInfo} + initialValue={learningObject.object_type !== undefined && learningObject.object_type !== null ? objTypes.filter((type) => type.name === learningObject.object_type)[0].id : null} + /> + </Grid> + + {/*------------------------------Idioma------------------------------------------*/} + <Grid item xs={12} style={{paddingBottom : "40px"}}> + <Idioma languages={languages} draftID={props.draftID} onBlurCallback={SendInfo} + initialValue={learningObject.language !== undefined ? learningObject.language.map((language) => language.name) : null} initialIDValues={learningObject.language !== undefined ? learningObject.language.map((language) => language.id) : null} + /> + </Grid> + + {/*------------------------------Botoes------------------------------------------*/} + <Grid item xs={12}> + <ButtonsDiv draftID={props.draftID} stepperControl={props.stepperControl}/> + </Grid> + + <Grid item xs={12} style={{marginTop : "20px"}}> + <span style={{marginTop : "20px", fontWeight : "200", color : "#a5a5a5", paddingLeft : "10px"}}> + * Campos obrigatórios + </span> + </Grid> + </form> + ) + : + ( + <LoadingSpinner text={"CARREGANDO"}/> + ) + } + </React.Fragment> ) } diff --git a/src/Components/UploadPageComponents/PartThree.js b/src/Components/UploadPageComponents/PartThree.js index b1eb3f564739b77c4c13ffb2e5e6a04a2699004d..e751a1805175b71d28da3251327e097dc6bba7c6 100644 --- a/src/Components/UploadPageComponents/PartThree.js +++ b/src/Components/UploadPageComponents/PartThree.js @@ -35,11 +35,13 @@ import { GrayButton, OrangeButton } from './StyledComponents'; import ModalCancelar from './ModalCancelar.js' import { getDefaultThumbnail } from '../HelperFunctions/getDefaultThumbnail' import { getRequest } from '../HelperFunctions/getAxiosConfig.js' +import ReCaptcha from 'react-recaptcha' export default function PartThree(props) { var moment = require('moment') const { state } = useContext(Store) const [loading, setLoading] = useState(false) + const [unavailableButton, setButtonAvailability] = useState(true); const [draft, setDraft] = useState({}) const [subjects, setSubjects] = useState('') @@ -84,6 +86,12 @@ export default function PartThree(props) { return (state.currentUser.roles.filter((role) => role.name === userRole).length > 0) } + function captchaVerified (response) { + if (response) { + setButtonAvailability(false) + } + } + return ( <React.Fragment> { @@ -205,8 +213,14 @@ export default function PartThree(props) { </span> </Grid> - <Grid item xs={windowWidth > 990 ? 6 : 12} style={{ paddingRight: "15px", paddingLeft: "15px", textAlign: windowWidth > 990 ? 'left' : 'center' }}> - <span>Recaptcha</span> + <Grid item xs={windowWidth > 990 ? 6 : 12} style={{ paddingRight: "15px", paddingLeft: "15px"}}> + <div style={{margin:"0 auto", width: "304px"}}> + { + //<ReCaptcha sitekey={process.env.REACT_APP_SITE_KEY} verifyCallback={captchaVerified} /> //when key set in env + //<ReCaptcha sitekey="6LfxuKUUAAAAAIzYpCzEtJyeE8QRjBYa44dvHlTX" verifyCallback={captchaVerified} /> //use this one on production + <ReCaptcha sitekey="6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI" verifyCallback={captchaVerified} /> //test key, from google, do not use this one on production + } + </div> </Grid> <Grid item xs={12} style={{ paddingRight: "15px", paddingLeft: "15px", marginTop: "30px", textAlign: 'center' }}> <GrayButton onClick={() => { props.stepperControl(-1) }}>VOLTAR</GrayButton> @@ -214,11 +228,23 @@ export default function PartThree(props) { { checkAccessLevel("partner") ? ( - <OrangeButton onClick={props.handlePost}>PUBLICAR RECURSO</OrangeButton> + unavailableButton ? ( + <GrayButton disabled={unavailableButton}>PUBLICAR RECURSO</GrayButton> + ) + : + ( + <OrangeButton onClick={props.handlePost}>PUBLICAR RECURSO</OrangeButton> + ) ) : ( - <OrangeButton onClick={props.handleSubmit}>SUBMETER RECURSO</OrangeButton> + unavailableButton ? ( + <GrayButton disabled={unavailableButton}>SUBMETER RECURSO</GrayButton> + ) + : + ( + <OrangeButton onClick={props.handleSubmit}>SUBMETER RECURSO</OrangeButton> + ) ) } diff --git a/src/Components/UploadPageComponents/PartTwo.js b/src/Components/UploadPageComponents/PartTwo.js index 221998c85f4e04e0a35204fcb89bd675b95b68f0..cf65a4de9952a0450b5bfedf71bd671511bcecbe 100644 --- a/src/Components/UploadPageComponents/PartTwo.js +++ b/src/Components/UploadPageComponents/PartTwo.js @@ -16,7 +16,7 @@ GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with Plataforma Integrada MEC. If not, see <http://www.gnu.org/licenses/>.*/ -import React, {useState, useEffect} from 'react' +import React, {useState, useEffect, useRef} from 'react' import Grid from '@material-ui/core/Grid'; import styled from 'styled-components' import DragAndDropThumbnail from './PartTwoComponents/DragAndDropThumbnail' @@ -33,6 +33,8 @@ import EditThumbnail from './PartTwoComponents/EditThumbnail.js' import DisplayThumbnail from './PartTwoComponents/DisplayThumbnail.js' import CustomCircularProgress from './PartTwoComponents/CustomCircularProgress'; import {getRequest, putRequest} from '../HelperFunctions/getAxiosConfig.js' +import SnackBar from '../../Components/SnackbarComponent'; +import LoadingSpinner from '../../Components/LoadingSpinner' export function LoadingDiv () { return ( @@ -43,27 +45,96 @@ export function LoadingDiv () { } export default function PartTwo (props) { + const didMountRef = useRef(false); + const [eduStages, setEduStages] = useState([]) const [subjects, setSubjects] = useState([]) const [themes, setThemes] = useState([]) + const [learningObject, setLearningObject] = useState({}) + + const [loading, toggleLoading] = useState(true) + + const [snackInfo, setSnackInfo] = useState({ + open: false, + text: '', + severity: '', + color: '', + }) + + function handleCloseSnackBar() { + const info = { + open: false, + text: '', + severity: '', + color: '', + } + handleSnackInfo(info) + } + + function handleSnackInfo(info) { + setSnackInfo({ + ...info + }) + } + function handleSuccess (data) { setSubjects(data.filter(subject => subject.theme === false).sort((a,b) => a.name > b.name ? 1 : -1)) setThemes(data.filter(subject => subject.theme === true).sort((a,b) => a.name > b.name ? 1 : -1)) } + function checkPartTwo (data) { + return ( + data.educational_stages.length !== 0 && + data.subjects.length !== 0 && + data.license !== null && + termsCheckbox + ) + } + + function handleSuccessGetFormData (data) { + if (checkPartTwo(data)) { + props.stepperControl(1) + } else { + const info = { + open: true, + text: 'Preencha todos os campos obrigatórios, inclusive o acordo de termos de uso e de propriedade intelectual!', + severity: 'warning', + color: '#FFC125', + } + handleSnackInfo(info) + } + } + + function handleSuccessfulGet (data) { + setLearningObject(data) + } + useEffect(() => { getRequest(`/educational_stages/`, (data) => {setEduStages(data)}, (error) => {console.log(error)}) getRequest(`/subjects/`, handleSuccess, (error) => {console.log(error)}) + + const url = `/learning_objects/${props.draftID}` + getRequest(url, handleSuccessfulGet, (error) => {console.log(error)}) }, []) + useEffect( () => { + if (didMountRef.current) { + console.log(learningObject); + toggleLoading(false) + } + else { + didMountRef.current = true; + } + }, [learningObject]) + /*------------------------Licenca------------------------*/ const [termsCheckbox, setChecked] = useState(false) - const toggleCheckbox = (event) => { - setChecked(event.target.checked) + const toggleCheckbox = () => { + setChecked(!termsCheckbox) } const [thumbnail, setThumbnail] = useState('') @@ -107,46 +178,79 @@ export default function PartTwo (props) { } } - const handleSubmit = () => { - props.stepperControl(1) - } + const handleSubmit = (e) => { + e.preventDefault(); + getRequest(`/learning_objects/${props.draftID}`, + handleSuccessGetFormData, + () => { + const info = { + open: true, + text: 'Não foi possível verificar o status da publicação!', + severity: 'error', + color: 'red', + } + handleSnackInfo(info) + } + ) + } return ( - <form style={{width : "100%"}} onSubmit={handleSubmit}> - <Grid item xs={12} style={{paddingBottom : "40px"}}> - {chooseRenderStageThumbnail()} - </Grid> - - <Grid item xs={12} style={{paddingBottom : "40px"}}> - <EducationalStage draftID={props.draftID} eduStages={eduStages} onBlurCallback={SendInfo}/> - </Grid> - - <SubjectsAndThemes draftID={props.draftID} subjects={subjects} themes={themes} onUploadPage={true} onBlurCallback={SendInfo}/> - - <Grid item xs={12} style={{paddingBottom : "40px"}}> - <Licenca draftID={props.draftID} onBlurCallback={SendInfo}/> - </Grid> - - <Grid item xs={12} style={{paddingBottom : "40px"}}> - <StyledFormControl required > - <StyledFormLabel component="legend" style={{fontSize : "14px", marginBottom : "10px"}} onClick={() => window.open("/termos/", "_blank")}> - <b>Confirme se você concorda com os <strong style={{color : "#ff7f00"}}>termos de uso e de propriedade intelectual</strong></b> - </StyledFormLabel> - <FormControlLabel label={<span className="label">Li e concordo com os termos de uso e de propriedade intelectual.</span>} control={<Checkbox checked={termsCheckbox} onChange={toggleCheckbox}/>} + <React.Fragment> + <SnackBar + snackbarOpen={snackInfo.open} + handleClose={handleCloseSnackBar} + severity={snackInfo.severity} + color={snackInfo.color} + text={snackInfo.text} + /> + { + !loading ? ( + <form style={{width : "100%"}} onSubmit={handleSubmit}> + <Grid item xs={12} style={{paddingBottom : "40px"}}> + {chooseRenderStageThumbnail()} + </Grid> + + <Grid item xs={12} style={{paddingBottom : "40px"}}> + <EducationalStage draftID={props.draftID} eduStages={eduStages} onBlurCallback={SendInfo} + initialValue={learningObject.educational_stages !== null ? learningObject.educational_stages.map((stage) => String(stage.id)) : null} + /> + </Grid> + + <SubjectsAndThemes draftID={props.draftID} subjects={subjects} themes={themes} onUploadPage={true} onBlurCallback={SendInfo} + initialValue={learningObject.subjects !== null ? learningObject.subjects.map(subject => String(subject.id)) : null} /> - </StyledFormControl> - </Grid> - - <Grid item xs={12}> - <ButtonsDiv draftID={props.draftID} stepperControl={props.stepperControl} onPartTwo={true}/> - </Grid> - - <Grid item xs={12} style={{marginTop : "20px"}}> - <span style={{marginTop : "20px", fontWeight : "200", color : "#a5a5a5", paddingLeft : "10px"}}> - * Campos obrigatórios - </span> - </Grid> - </form> + + <Grid item xs={12} style={{paddingBottom : "40px"}}> + <Licenca draftID={props.draftID} onBlurCallback={SendInfo} initialValue={learningObject.license ? learningObject.license.id : null}/> + </Grid> + + <Grid item xs={12} style={{paddingBottom : "40px"}}> + <StyledFormControl required > + <StyledFormLabel component="legend" style={{fontSize : "14px", marginBottom : "10px"}} onClick={() => window.open("/termos/", "_blank")}> + <b>Confirme se você concorda com os <strong style={{color : "#ff7f00"}}>termos de uso e de propriedade intelectual</strong></b> + </StyledFormLabel> + <FormControlLabel label={<span className="label">Li e concordo com os termos de uso e de propriedade intelectual.</span>} control={<Checkbox checked={termsCheckbox} onChange={toggleCheckbox}/>} + /> + </StyledFormControl> + </Grid> + + <Grid item xs={12}> + <ButtonsDiv draftID={props.draftID} stepperControl={props.stepperControl} onPartTwo={true}/> + </Grid> + + <Grid item xs={12} style={{marginTop : "20px"}}> + <span style={{marginTop : "20px", fontWeight : "200", color : "#a5a5a5", paddingLeft : "10px"}}> + * Campos obrigatórios + </span> + </Grid> + </form> + ) + : + ( + <LoadingSpinner text={"CARREGANDO"}/> + ) + } + </React.Fragment> ) } diff --git a/src/Components/UploadPageComponents/SendInfo.js b/src/Components/UploadPageComponents/SendInfo.js index ebcb1b95f377fe018e3a178716ff7d1621009dba..606a7f72c7bb97e7b7eda26a95a76ca5bcc29e4a 100644 --- a/src/Components/UploadPageComponents/SendInfo.js +++ b/src/Components/UploadPageComponents/SendInfo.js @@ -23,8 +23,8 @@ export function SendInfo (fieldName, payload, draftID) { const key = fieldName let value = payload if (key === "tags") { - value = payload.map( (tag, index) => - index = { "name" : tag} + value = payload.map( (tag, index) => { + return (index = { "name" : tag})} ) } diff --git a/src/Pages/EditLearningObjectPage.js b/src/Pages/EditLearningObjectPage.js index 5f8e84fd37ca9631c73f1ede50177413be608507..c785bf3564d672834c0ed00f964fcc139040a8c5 100644 --- a/src/Pages/EditLearningObjectPage.js +++ b/src/Pages/EditLearningObjectPage.js @@ -24,6 +24,7 @@ import Grid from '@material-ui/core/Grid'; import UploadFileWrapper from '../Components/UploadPageComponents/UploadFileWrapper.js' import Alert from '../Components/Alert.js'; import Snackbar from '@material-ui/core/Snackbar'; +import {Redirect} from 'react-router-dom' import {GreyButton, OrangeButton, InfoBox} from '../Components/UploadPageComponents/StyledComponents.js' import {Background} from '../Components/UploadPageComponents/StyledComponents' import LoadingSpinner from '../Components/LoadingSpinner' @@ -46,6 +47,7 @@ export default function EditLearningObjectPage (props) { const recursoId = props.match.params.recursoId const {state} = useContext(Store) const [learningObject, setLearningObject] = useState({}) + const [publisherDeletedObject, toggleDeleted] = useState(false) const [objTypes, setObjTypes] = useState([]) const [languages, setLanguages] = useState([]) const [eduStages, setEduStages] = useState([]) @@ -115,7 +117,7 @@ export default function EditLearningObjectPage (props) { function handleSuccessfulDelete (data) { toggleSnackbar(true) - props.history.push("/") + toggleDeleted(true) } const handleDelete = () => { @@ -237,6 +239,15 @@ export default function EditLearningObjectPage (props) { } return ( <React.Fragment> + { + publisherDeletedObject && + <Redirect + to={{ + pathname: "/perfil", + state: 1 + }} + /> + } <Snackbar open={snackbarOpen} autoHideDuration={1000} onClose={() => {toggleSnackbar(false)}} anchorOrigin = {{ vertical:'top', horizontal:'right' }} > @@ -276,7 +287,7 @@ export default function EditLearningObjectPage (props) { </Grid> <Grid item xs={12} style={{paddingBottom : "40px"}}> - <Keywords draftID={learningObject.id} initialValue={learningObject.tags.map((tag) => tag.name)} + <Keywords draftID={learningObject.id} initialValue={learningObject.tags !== undefined ? learningObject.tags.map((tag) => tag.name) : null} onBlurCallback={onBlurCallback}/> </Grid> @@ -296,20 +307,21 @@ export default function EditLearningObjectPage (props) { <Grid item xs={12} style={{paddingBottom : "40px"}}> <TipoDeRecurso objTypes={objTypes} draftID={learningObject.id} - initialValue={learningObject.object_type !== null ? objTypes.filter((type) => type.name === learningObject.object_type)[0].id : null} onBlurCallback={onBlurCallback} /> + initialValue={learningObject.object_type !== undefined && learningObject.object_type !== null ? objTypes.filter((type) => type.name === learningObject.object_type)[0].id : null} onBlurCallback={onBlurCallback} /> </Grid> <Grid item xs={12} style={{paddingBottom : "40px"}}> - <Idioma languages={languages} draftID={learningObject.id} initialValue={learningObject.language.map((language) => language.name)} initialIDValues={learningObject.language.map((language) => language.id)} - onBlurCallback={onBlurCallback} /> + <Idioma languages={languages} draftID={learningObject.id} + initialValue={learningObject.language !== undefined ? learningObject.language.map((language) => language.name) : null} initialIDValues={learningObject.language !== undefined ? learningObject.language.map((language) => language.id) : null} + onBlurCallback={onBlurCallback} /> </Grid> <Grid item xs={12} style={{paddingBottom : "40px"}}> - <EducationalStage draftID={learningObject.id} eduStages={eduStages} initialValue={learningObject.educational_stages.map((stage) => String(stage.id))} onBlurCallback={onBlurCallback} + <EducationalStage draftID={learningObject.id} eduStages={eduStages} initialValue={learningObject.educational_stages !== null ? learningObject.educational_stages.map((stage) => String(stage.id)) : null} onBlurCallback={onBlurCallback} /> </Grid> - <SubjectsAndThemes draftId={learningObject.id} subjects={subjects} initialValue={learningObject.subjects.map(subject => String(subject.id))} onBlurCallback={onBlurCallback}/> + <SubjectsAndThemes draftId={learningObject.id} subjects={subjects} initialValue={learningObject.subjects !== null ? learningObject.subjects.map(subject => String(subject.id)) : null} onBlurCallback={onBlurCallback}/> <Grid item xs={12} style={{paddingBottom : "40px"}}> <Licenca draftID={learningObject.id} initialValue={learningObject.license ? learningObject.license.id : null} onBlurCallback={onBlurCallback}/> diff --git a/src/Pages/PageNotFound.js b/src/Pages/PageNotFound.js new file mode 100644 index 0000000000000000000000000000000000000000..7e9a5b0ca55aa05bc584bad105e8ebe0f3f306e5 --- /dev/null +++ b/src/Pages/PageNotFound.js @@ -0,0 +1,44 @@ +/*Copyright (C) 2019 Centro de Computacao Cientifica e Software Livre +Departamento de Informatica - Universidade Federal do Parana + +This file is part of Plataforma Integrada MEC. + +Plataforma Integrada MEC is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Plataforma Integrada MEC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with Plataforma Integrada MEC. If not, see <http://www.gnu.org/licenses/>.*/ + +import React from 'react'; +import styled from "styled-components"; + +export default function PageNotFound (props) { + return ( + <div> + <link + href="https://fonts.googleapis.com/css?family=Roboto:100,400,500&display=swap" + rel="stylesheet" + /> + <StyledDiv><span style={{ fontSize: '50px' }}>Desculpe</span></StyledDiv> + <StyledDiv><span style={{ fontSize: '30px' }}>Não foi possível encontrar a página que você está procurando.</span></StyledDiv> + <StyledDiv><a href='/'><span style={{ fontSize: '20px' }}>Voltar para a página inicial</span></a></StyledDiv> + </div> + ) +} + +const StyledDiv = styled('div')` + width: 100%; + margin-top: 70px; + margin-bottom: 70px; + justify-content : space-evenly; + display: flex; + color: #757575; + text-align:center; +` \ No newline at end of file diff --git a/src/Pages/PublicationPermissionsPage.js b/src/Pages/PublicationPermissionsPage.js index d7dcb6ac34900cb4155629e3382997be37650f10..52bdbd8a8caf5b3b2ff7cfb465b230ac120206bb 100644 --- a/src/Pages/PublicationPermissionsPage.js +++ b/src/Pages/PublicationPermissionsPage.js @@ -95,8 +95,7 @@ export default function PermissionsContainer(props) { temp[e.target.name] = e.target.value; setRadioValues(temp); - - setCheckboxAvailability(!(radios.radio1 && radios.radio2 && radios.radio3)); + setCheckboxAvailability(!(radios.radio1 === "Não" && radios.radio2 === "Não" && radios.radio3 === "Não")); }; const handleAgreement = () => { @@ -144,7 +143,7 @@ export default function PermissionsContainer(props) { <div> <div style={{ fontSize: "14px" }}> <LabeledCheckbox - disabledCheckbox={unavailableCheckbox} + disabled={unavailableCheckbox} label={ <Styledspan> Li e permito a publicação do meu recurso na @@ -159,9 +158,9 @@ export default function PermissionsContainer(props) { style={{ justifyContent: "center", display: "flex" }} > <Button - disabled={unavailableButton} + disabled={unavailableButton || unavailableCheckbox} style={ - unavailableButton + unavailableButton || unavailableCheckbox ? { backgroundColor: "#e9e9e9" } : { backgroundColor: "#00bcd4" } } @@ -169,7 +168,7 @@ export default function PermissionsContainer(props) { > <Styledspan style={ - unavailableButton + unavailableButton || unavailableCheckbox ? { color: "#666666", fontWeight : "600" } : { color: "#ffffff", fontWeight : "600" } } diff --git a/src/index.css b/src/index.css index cafa7e0dbbf7013e6bee58a493a09ede5a3819c5..0e81d082e9db63e3b8940c84cdff4756272f0d94 100755 --- a/src/index.css +++ b/src/index.css @@ -17,6 +17,9 @@ You should have received a copy of the GNU Affero General Public License along with Plataforma Integrada MEC. If not, see <http://www.gnu.org/licenses/>.*/ body { + width: 100%; + height: 100%; + overflow-x: hidden; margin: 0; padding: 0; overflow-x: hidden;