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/CollectionCommentSection.js b/src/Components/CollectionCommentSection.js index 24d4da5ca2c1450154c8a1ea2ae8f30aafd95933..0db277fe8a16bcb5c108bbb50ef4f2c39406b150 100644 --- a/src/Components/CollectionCommentSection.js +++ b/src/Components/CollectionCommentSection.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, { useRef, useState, useEffect, Fragment } from 'react'; +import React, { useRef, useState, useEffect, Fragment, useContext } from 'react'; import { Grid } from '@material-ui/core'; import Card from '@material-ui/core/Card'; import Button from '@material-ui/core/Button'; @@ -33,9 +33,11 @@ import SignUpModal from './SignUpModal.js'; import LoginModal from './LoginModal.js'; import SnackBarComponent from './SnackbarComponent'; import CircularProgress from '@material-ui/core/CircularProgress'; +import { apiDomain } from '../env'; +import { Store } from '../Store' export default function CollectionCommentSection(props) { - + const { state } = useContext(Store) const [post_snack_open, setPostSnackOpen] = useState(false); const [delete_snack_open, setDeleteSnackOpen] = useState(false); const [render_state, setRenderState] = useState(false); @@ -134,29 +136,31 @@ export default function CollectionCommentSection(props) { } const CollectionComments = () => { return ( - <div> - <Title>{reviews.length} {reviews.length === 1 ? "Relato" : "Relatos"} sobre a Coleção</Title> + <ComentariosBox> + <h3>{reviews.length} {reviews.length !== 1 ? 'Relatos' : 'Relato'} sobre o uso do Recurso</h3> {reviews.map(r => { return ( - <Comment - isCollection={false} - rerenderCallback={forceUpdate} - objectID={props.id} - reviewID={r.id} - reviewRatings={r.review_ratings} - authorID={r.user.id} - rating={r.rating_average} - authorName={r.user.name} - authorAvatar={r.user.avatar} - description={r.description} - createdAt={r.created_at} - handleSnackbar={handleDeleteSnackbar} - handlePost={handlePostSnackbar} - recurso={false} - /> + <div className="comentario-template" key={r.created_at}> + <Comment + isCollection={false} + rerenderCallback={forceUpdate} + objectID={props.id} + reviewID={r.id} + reviewRatings={r.review_ratings} + authorID={r.user.id} + rating={r.rating_average} + authorName={r.user.name} + authorAvatar={r.user.avatar} + description={r.description} + createdAt={r.created_at} + handleSnackbar={handleDeleteSnackbar} + handlePost={handlePostSnackbar} + recurso={false} + /> + </div> ); })} - </div> + </ComentariosBox> ); } @@ -191,12 +195,21 @@ export default function CollectionCommentSection(props) { props.currentUserId ? <Fragment> <Title>Conte sua experiência com a coleção</Title> - <CommentForm - colecao - recursoId={props.id} - handleSnackbar={handlePostSnackbar} - rerenderCallback={forceUpdate} - /> + <Grid container style={{ paddingTop: "20px" }} spacing={1}> + <Grid item xs={12} sm={2} style={{ paddingLeft: "15px", paddingRight: "15px" }}> + <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}> + <Avatar src={apiDomain + state.currentUser.avatar} alt="user avatar" /> + </div> + </Grid> + <Grid item xs={12} sm={10}> + <CommentForm + colecao + recursoId={props.id} + handleSnackbar={handlePostSnackbar} + rerenderCallback={forceUpdate} + /> + </Grid> + </Grid> </Fragment> : <Grid item xs={12}> @@ -242,6 +255,37 @@ export default function CollectionCommentSection(props) { ); } +const ComentariosBox = styled.div` + display : flex; + flex-direction : column; + padding : 20px; + width : 100%; + + h3 { + font-family: 'Roboto Light','Roboto Regular',Roboto; + font-weight: 300; + font-style: normal; + color:#666; + font-size: 1.857em; + margin: 15px 2%; + text-align : flex-start; + } + + .comentario-template { + margin-top: 5px; + padding : 20px 0; + border-bottom : 1px solid #f4f4f4; + } +` + +const Avatar = styled.img` + height: 60px; + width: 60px; + border-radius: 50%; + margin-left: 2%; + margin-top: 5%; +` + const LoadingDiv = styled.div` margin: 1em; display: flex; @@ -274,13 +318,13 @@ const LogInToComment = styled.div` ` const CommentAreaContainer = styled(Grid)` - margin-left: 10%; - margin-right: 10%; + padding: 10px; ` const CommentAreaCard = styled(Card)` padding: 45px; ` const Title = styled.h1` + text-align: center; font-weight: 100; color: #666; ` diff --git a/src/Components/Comment.js b/src/Components/Comment.js index 2a77cb0cee1f6e44c0d4d5ba92e7c382ae1cb37c..4388ec758a2cdc81a97f8b61f7fb3765b3ef5805 100644 --- a/src/Components/Comment.js +++ b/src/Components/Comment.js @@ -34,7 +34,7 @@ import ModalExcluir from './ModalExcluirComentario.js' import { putRequest, deleteRequest } from './HelperFunctions/getAxiosConfig' export default function Comment(props) { - + console.log(props) /* Required props: rerenderCallback = callback function to trigger re-render on parent component @@ -104,7 +104,7 @@ export default function Comment(props) { function handleSuccessDeleteComment(data) { props.rerenderCallback() - props.handleSnackbar() + props.handleSnackbar(3) } const deleteComment = () => { @@ -120,131 +120,149 @@ export default function Comment(props) { toggleModal(false) } + if (props.authorID) + return ( + <React.Fragment> + <ModalExcluir + open={modalOpen} handleClose={() => { toggleModal(false) }} + handleConfirm={deleteComment} + /> + <Grid container style={{ paddingLeft: "20px" }}> - return ( - <React.Fragment> - <ModalExcluir - open={modalOpen} handleClose={() => { toggleModal(false) }} - handleConfirm={deleteComment} - /> - <Grid container style={{ paddingLeft: "20px" }}> - - <Grid item xs={1}> - { - props.authorID && - <AvatarDiv> - <Link to={'/usuario-publico/' + props.authorID}> - <img src={props.authorAvatar ? apiDomain + props.authorAvatar : noAvatar} alt="author avatar" /> - </Link> - </AvatarDiv> - } - </Grid> - - <Grid item xs={10}> - <Comentario> - <div className="star-rating-container"> - <Rating - name="read-only" - value={props.rating} - readOnly - size="small" - style={{ color: "#666" }} - emptyIcon={<StarBorderIcon fontSize="inherit" style={{ color: "#a5a5a5" }} />} - /> - </div> - + <Grid item xs={1}> { - props.name && - <strong>{props.name}</strong> + props.authorID && + <AvatarDiv> + <Link to={'/usuario-publico/' + props.authorID}> + <img src={props.authorAvatar ? apiDomain + props.authorAvatar : noAvatar} alt="author avatar" /> + </Link> + </AvatarDiv> } + </Grid> + + <Grid item xs={10}> + <Comentario> + <div className="star-rating-container"> + <Rating + name="read-only" + value={props.rating} + readOnly + size="small" + style={{ color: "#666" }} + emptyIcon={<StarBorderIcon fontSize="inherit" style={{ color: "#a5a5a5" }} />} + /> + </div> - <div> { - editando ? - ( - <React.Fragment> - <div style={{ marginTop: "5%", padding: "2px" }}> - <StyledTextField - colecao={!props.recurso} - id="input-comentario" - label={"Editar Comentário"} - margin="normal" - value={comment.value} - multiline={true} - rows="5" - onChange={(e) => { handleChange(e) }} - style={{ width: "100%" }} - /> - </div> - <div style={{ float: "right" }}> - <StyledButton - style={props.recurso ? { backgroundColor: "#ff7f00" } : { backgroundColor: "#673ab7" }} - onClick={() => { setEditando(false) }} - > - Fechar - </StyledButton> - <StyledButton - style={props.recurso ? { backgroundColor: "#ff7f00" } : { backgroundColor: "#673ab7" }} - onClick={() => updateComment()} - > - Salvar + props.name && + <strong>{props.name}</strong> + } + + <div> + { + editando ? + ( + <React.Fragment> + <div style={{ marginTop: "5%", padding: "2px" }}> + <StyledTextField + colecao={!props.recurso} + id="input-comentario" + label={"Editar Comentário"} + margin="normal" + value={comment.value} + multiline={true} + rows="5" + onChange={(e) => { handleChange(e) }} + style={{ width: "100%" }} + /> + </div> + <div style={{ float: "right" }}> + <StyledButton + style={props.recurso ? { backgroundColor: "#ff7f00" } : { backgroundColor: "#673ab7" }} + onClick={() => { setEditando(false) }} + > + Fechar </StyledButton> - </div> - </React.Fragment> - ) - : - ( - <React.Fragment> - <p> - { - props.authorID && - <Link - to={'/usuario-publico/' + props.authorID} - style={{ - fontWeight: "bolder", - color: props.recurso ? "#ff7f00" : "#673ab7" - }} + <StyledButton + style={props.recurso ? { backgroundColor: "#ff7f00" } : { backgroundColor: "#673ab7" }} + onClick={() => updateComment()} > - {props.authorName} - </Link> - } + Salvar + </StyledButton> + </div> + </React.Fragment> + ) + : + ( + <React.Fragment> + <p> + { + props.authorID && + <Link + to={'/usuario-publico/' + props.authorID} + style={{ + fontWeight: "bolder", + color: props.recurso ? "#ff7f00" : "#673ab7" + }} + > + {props.authorName} + </Link> + } : {displayedComment} - </p> - { - props.authorID !== state.currentUser.id && + </p> <span className="date"> {moment(props.createdAt).format("DD/MM/YYYY")} </span> - } - </React.Fragment> - ) - } - </div> + </React.Fragment> + ) + } + </div> - </Comentario> + </Comentario> + </Grid> + + { + props.authorID === state.currentUser.id && + <Grid item xs={1}> + <StyledDiv> + <Button onClick={handleClick}><EditIcon /></Button> + <Menu + id="simple-menu" + anchorEl={anchorEl} + keepMounted + open={Boolean(anchorEl)} + onClose={handleClose} + > + <MenuItem onClick={() => { setEditando(true); handleClose() }}>Editar</MenuItem> + <MenuItem onClick={() => { toggleModal(true); handleClose() }}>Excluir</MenuItem> + </Menu> + </StyledDiv> + </Grid> + } </Grid> + </React.Fragment> + ) + else + return ( + <Grid container style={{ paddingLeft: "20px" }} justify='center' alignItems='center '> - { - props.authorID === state.currentUser.id && - <Grid item xs={1}> - <StyledDiv> - <Button onClick={handleClick}><EditIcon /></Button> - <Menu - id="simple-menu" - anchorEl={anchorEl} - keepMounted - open={Boolean(anchorEl)} - onClose={handleClose} - > - <MenuItem onClick={() => { setEditando(true); handleClose() }}>Editar</MenuItem> - <MenuItem onClick={() => { toggleModal(true); handleClose() }}>Excluir</MenuItem> - </Menu> - </StyledDiv> - </Grid> - } + <Grid item xs={1}> + { + <AvatarDiv> + <img src={noAvatar} alt="author avatar" /> + </AvatarDiv> + } + </Grid> + + <Grid item xs={10}> + <Comentario> + <p> + O usuário que fez esse comentário deletou a conta. + </p> + </Comentario> + </Grid> </Grid> - </React.Fragment> - ) + ) } const StyledTextField = styled(TextField)` @@ -274,9 +292,7 @@ const StyledButton = styled(Button)` ` const Comentario = styled.div` - @media screen and (max-width: 990px) { - padding-left : 55px !important; - } + padding-left : 55px !important; font-size : 14px; .star-rating-container { diff --git a/src/Components/HelperFunctions/getAxiosConfig.js b/src/Components/HelperFunctions/getAxiosConfig.js index 0edd58872210005aad1996a75d85241d09684424..8ced7d5be70b3d08d7b2cb3f7864f801b8c2d484 100644 --- a/src/Components/HelperFunctions/getAxiosConfig.js +++ b/src/Components/HelperFunctions/getAxiosConfig.js @@ -9,7 +9,9 @@ export function getAxiosConfigFromJSON () { export function updateHeaders (newHeaders) { - localStorage.setItem('@portalmec/accessToken', newHeaders['access-token']) + let newToken = getNewAccessToken(newHeaders['access-token']) + + localStorage.setItem('@portalmec/accessToken', newToken) let auth_headers = JSON.parse(localStorage.getItem('@portalmec/auth_headers')) /*const auth_headers = { @@ -79,16 +81,27 @@ function checkPreviousTokens (new_token) { } +function getNewAccessToken (newAccessToken) { + if (!newAccessToken || newAccessToken.trim().length === 0) { + return localStorage.getItem('@portalmec/accessToken') + } + else { + return newAccessToken + } +} + + function updateAccessToken (newAccessToken) { + let newToken = getNewAccessToken(newAccessToken) - if (checkPreviousTokens(newAccessToken)) { + if (checkPreviousTokens(newToken)) { - localStorage.setItem('@portalmec/accessToken', newAccessToken) + localStorage.setItem('@portalmec/accessToken', newToken) let auth_headers = JSON.parse(localStorage.getItem('@portalmec/auth_headers')) if (auth_headers) { - auth_headers['access-token'] = newAccessToken + auth_headers['access-token'] = newToken } localStorage.setItem('@portalmec/auth_headers', JSON.stringify(auth_headers)) @@ -220,7 +233,7 @@ export async function fetchAllRequest (urls, onSuccess, onError) { updateAccessToken(res.headers.get('access-token')) } let json = await res.json().catch(err => { - return null; + return {}; }) data.push(json) headers.push(res.headers) @@ -264,6 +277,7 @@ export const validateGoogleLoginToken = (url, config, onSuccess, onError) => { } localStorage.setItem('@portalmec/auth_headers', JSON.stringify(auth_headers)) + localStorage.setItem('@portalmec/accessToken', auth_headers["access-token"]) return response.json().catch(err => { return {}; @@ -298,6 +312,7 @@ export async function authentication (url, payload, onSuccess, onError) { } localStorage.setItem('@portalmec/auth_headers', JSON.stringify(auth_headers)) + localStorage.setItem('@portalmec/accessToken', auth_headers["access-token"]) let json = await response.json().catch(err => { return {}; 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/CommentForm.js b/src/Components/ResourcePageComponents/CommentForm.js index 0b1c38ba693b98b03c689f18035a6f81478d9cd9..0272d76fc6f63b264b9197d390818729caa46cc1 100644 --- a/src/Components/ResourcePageComponents/CommentForm.js +++ b/src/Components/ResourcePageComponents/CommentForm.js @@ -1,4 +1,4 @@ -import React, {useState} from 'react' +import React, { useState } from 'react' import styled from 'styled-components' import Rating from '@material-ui/lab/Rating'; import StarIcon from '@material-ui/icons/Star'; @@ -6,27 +6,27 @@ import TextField from "@material-ui/core/TextField"; import { Button } from '@material-ui/core'; import EditIcon from '@material-ui/icons/Edit'; import Grid from '@material-ui/core/Grid'; -import {postRequest} from '../HelperFunctions/getAxiosConfig' +import { postRequest } from '../HelperFunctions/getAxiosConfig' -export default function CommentForm (props) { +export default function CommentForm(props) { const [rating, setRating] = useState({ - error : true, - value : 0 + error: true, + value: 0 }) const [comment, setComment] = useState({ - error : false, - value : '' + error: false, + value: '' }) const handleChange = (e) => { const userInput = e.target.value const flag = (userInput.length === 0 ? true : false); - setComment({...comment, error : flag, value : userInput}) + setComment({ ...comment, error: flag, value: userInput }) } const [attemptedSubmit, setAttempt] = useState(false) - function handleSuccess (data) { + function handleSuccess(data) { props.handleSnackbar(1) props.rerenderCallback() } @@ -40,18 +40,18 @@ export default function CommentForm (props) { const url = `/${type}/${props.recursoId}/reviews` let payload = { - "review" : { - "description" : finalComment.value, - "review_ratings_attributes" : [ + "review": { + "description": finalComment.value, + "review_ratings_attributes": [ { - "rating_id" : 1, - "value" : finalRating.value + "rating_id": 1, + "value": finalRating.value } ] } } - postRequest(url, payload, handleSuccess, (error) => {console.log(error)}) + postRequest(url, payload, handleSuccess, (error) => { console.log(error) }) } else { setAttempt(true) @@ -65,42 +65,42 @@ export default function CommentForm (props) { </label> <div className="stars-container"> <Rating - name="avaliacao-estrelas" - value={rating.value} - precision={0.5} - style={{color:"#ff9226"}} - onChange = {(e, newValue) => {setRating({...rating, error : newValue === null ? true : false, value : newValue})}} - emptyIcon={<StarIcon fontSize="inherit" style={{color : "#666"}} />} - getLabelText={(value) => {return(value + ' Estrela' + (value !== 1 ? 's' : ''))}} + name="avaliacao-estrelas" + value={rating.value} + precision={0.5} + style={{ color: "#ff9226" }} + onChange={(e, newValue) => { setRating({ ...rating, error: newValue === null ? true : false, value: newValue }) }} + emptyIcon={<StarIcon fontSize="inherit" style={{ color: "#666" }} />} + getLabelText={(value) => { return (value + ' Estrela' + (value !== 1 ? 's' : '')) }} /> </div> - <div className="star-alert" style={attemptedSubmit ? {visibility : "visible"} : {visibility : "hidden" }}>{props.recurso ? "Avalie se o recurso foi útil." : "Avalie se esta coleção foi útil."}</div> + <div className="star-alert" style={attemptedSubmit ? { visibility: "visible" } : { visibility: "hidden" }}>{props.recurso ? "Avalie se o recurso foi útil." : "Avalie se esta coleção foi útil."}</div> <Grid container> <Grid item xs={12} md={9}> - <StyledTextField - colecao={!props.recurso} - value={comment.value} - multiline - rows="5" - error={comment.error} - label={props.recurso ? "Escreva aqui a sua experiência com este Recurso" : "Escreva aqui a sua experiência com esta Coleção"} - onChange={e => handleChange(e)} - required={true} - help = {comment.error ? (props.recurso ? "Escreva aqui a sua experiência com este Recurso" : "Escreva aqui a sua experiência com esta Coleção") : ''} - /> + <StyledTextField + colecao={!props.recurso} + value={comment.value} + multiline + rows="5" + error={comment.error} + label="Relate sua experiência" + onChange={e => handleChange(e)} + required={true} + help={comment.error ? (props.recurso ? "Escreva aqui a sua experiência com este Recurso" : "Escreva aqui a sua experiência com esta Coleção") : ''} + /> </Grid> <Grid item xs={12} md={3}> - <div style={{height : "100%", display : "flex", flexDirection : "column", justifyContent : "flex-end"}}> + <div style={{ height: "100%", display: "flex", flexDirection: "column", justifyContent: "flex-end" }}> { props.recurso ? - ( - <OrangeButton type="submit">Publicar</OrangeButton> - ) - : - ( - <PurpleButton type="submit"><EditIcon/>Enviar</PurpleButton> - ) + ( + <OrangeButton type="submit">Publicar</OrangeButton> + ) + : + ( + <PurpleButton type="submit"><EditIcon />Enviar</PurpleButton> + ) } </div> </Grid> @@ -149,7 +149,7 @@ const StyledTextField = styled(TextField)` } .MuiInput-underline::after { - border-bottom: ${props => props.colecao ? "2px solid #673ab7" : "2px solid rgb(255,127,0)" }; + border-bottom: ${props => props.colecao ? "2px solid #673ab7" : "2px solid rgb(255,127,0)"}; } label.Mui-focused.Mui-error { diff --git a/src/Components/ResourcePageComponents/CommentsArea.js b/src/Components/ResourcePageComponents/CommentsArea.js index 37db3024dfaf7c41c6d0f54976d9e74bef267cca..0ba2d12a188d45a5516b20611d2aee1679846fbd 100644 --- a/src/Components/ResourcePageComponents/CommentsArea.js +++ b/src/Components/ResourcePageComponents/CommentsArea.js @@ -55,6 +55,10 @@ export default function CommentsArea(props) { setLogin(!loginOpen) } + const handlePost = () => { + props.handleSnackbar(2) + } + const toggleSnackbar = (event, reason) => { if (reason === 'clickaway') { return; @@ -94,11 +98,13 @@ export default function CommentsArea(props) { <Grid item xs={12} > <GrayContainer> <h3>Conte sua experiência com o Recurso</h3> - <Grid container style={{ paddingTop: "20px" }}> - <Grid item xs={2} style={{ paddingLeft: "15px", paddingRight: "15px" }}> - <img src={apiDomain + state.currentUser.avatar} className="minha-imagem" alt="user avatar" /> + <Grid container style={{ paddingTop: "20px" }} spacing={1}> + <Grid item xs={12} sm={2} style={{ paddingLeft: "15px", paddingRight: "15px" }}> + <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}> + <img src={apiDomain + state.currentUser.avatar} className="minha-imagem" alt="user avatar" /> + </div> </Grid> - <Grid item xs={10}> + <Grid item xs={12} sm={10}> <CommentForm recursoId={props.recursoId} handleSnackbar={props.handleSnackbar} @@ -148,6 +154,7 @@ export default function CommentsArea(props) { recurso={true} reviewID={comentario.id} objectID={props.recursoId} + handlePost={handlePost} rerenderCallback={forceUpdate} handleSnackbar={props.handleSnackbar} /> @@ -175,11 +182,13 @@ export default function CommentsArea(props) { } const LoadingDiv = styled.div` - margin: 1em; + width: 100%; + margin: 1em auto; display: flex; justify-content: center; align-items: center; .loading{ + align-self: center; color: #ff7f00; size: 24px; } @@ -202,6 +211,7 @@ const ComentariosBox = styled.div` } .comentario-template { + margin-top: 5px; padding : 20px 0; border-bottom : 1px solid #f4f4f4; } @@ -237,9 +247,6 @@ const LogInToComment = styled.div` const GrayContainer = styled.div` background-color : #fafafa; font-weight : 400; - display : flex; - flex-direction : column; - justify-content : space-between; font-size : 14px; padding-bottom : 20px; @media screen and (min-width : 990px) { @@ -252,6 +259,7 @@ const GrayContainer = styled.div` h3 { font-family : 'Roboto Light','Roboto Regular',Roboto; + text-align: center; font-weight: 300; font-style: normal; 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;