diff --git a/src/App.js b/src/App.js index c4ffd8534a3bf1739d7dd72283f1b59a95747895..e64e7b9782a9864b19d069e20e54f0c8cde68361 100644 --- a/src/App.js +++ b/src/App.js @@ -43,6 +43,7 @@ import TabPlataformaMEC from './Pages/TabsHelp/TabPlataformaMEC'; import ItemStore from './Pages/ItemStore.js' import EditProfilePage from './Pages/EditProfilePage.js' import PublicUserPage from './Pages/PublicUserPage.js' +import CollectionPage from './Pages/CollectionPage.js' export default function App(){ // eslint-disable-next-line @@ -101,6 +102,7 @@ export default function App(){ <Route path="/recuperar-senha" component={PasswordRecoveryPage}/> <Route path='/professor' component={PageProfessor}/> <Route path='/loja' component={ItemStore} /> + <Route path='/colecao-do-usuario/:id' component={CollectionPage} /> </Switch> <EcFooter/> <GNUAGPLfooter/> diff --git a/src/Components/CollectionAuthor.js b/src/Components/CollectionAuthor.js index 77693108ae1dfcdbc60457da066b8779c20a28f3..cc32c2fe89a6d2b7d5bda29e6e54e2a4640263e1 100644 --- a/src/Components/CollectionAuthor.js +++ b/src/Components/CollectionAuthor.js @@ -19,6 +19,8 @@ along with Plataforma Integrada MEC. If not, see <http://www.gnu.org/licenses/> import React from 'react'; import { Grid } from '@material-ui/core'; import styled from 'styled-components'; +import CircularProgress from '@material-ui/core/CircularProgress'; +import LinearProgress from '@material-ui/core/LinearProgress'; export default function CollectionAuthor(props) { @@ -27,9 +29,17 @@ export default function CollectionAuthor(props) { direction="column" justify="center" alignItems="center"> - <UserAvatar src={props.imgsrc}/> + {props.imgsrc ? + <UserAvatar src={props.imgsrc}/> + : + <CircularProgress color="secondary"/> + } <InfoText>Coleção organizada por:</InfoText> - <UserName>{props.name}</UserName> + {props.name ? + <UserName>{props.name}</UserName> + : + <CircularProgress /> + } </Grid> ); } diff --git a/src/Components/CollectionCommentSection.js b/src/Components/CollectionCommentSection.js index a3fb2f91ebac3960fc95fe58fbca9cbd3f4e8859..7360158cbe59c85932850b89b1bb50d30bbcfc2a 100644 --- a/src/Components/CollectionCommentSection.js +++ b/src/Components/CollectionCommentSection.js @@ -16,50 +16,16 @@ 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 } from 'react'; +import React, { useState, useEffect } from 'react'; import { Grid } from '@material-ui/core'; import styled from 'styled-components'; -import Button from '@material-ui/core/Button'; -import Card from '@material-ui/core/Card'; +import axios from 'axios'; +import { apiUrl } from '../env'; -export default function CollectionCommentSection() { - return ( - <BackDiv> - <CommentContainer container direction="row" justify="center" alignItems="center"> - <Grid item style={{width: '100%'}} xs={12}> - <Title>Conte sua experiência com a Coleção</Title> - <Divider /> - </Grid> - <Grid item> - avatar - </Grid> - <Grid item> - avaliação (componente separado, grid container direction="column" e no final tem um grid item container direction="row" (escrever + botão de enviar)) - </Grid> +export default function CollectionCommentSection(props) { - </CommentContainer> - componente de compartilhar opiniao com a rede, grid container direction="column" + modal - n esqueça que tem que colocar modal de login em tudo se a pessoa n estiver logada - </BackDiv> + return ( + <Grid container direction="row" justify="center" alignItems="center"> + </Grid> ); } - -const BackDiv = styled(Card)` - background-color: white; - padding: 7px; -` -const CommentContainer = styled(Grid)` - background-color: #f4f4f4; -` -const Title = styled.p` - color: rgb(102, 102, 102); - font-weight: 300; - font-size: 1.5em; - margin-left: 30px; -` -const Divider = styled.div` - margin-top: 10px; - margin-bottom: 10px; - background-color: rgb(102, 102, 102); - height: 0.5px; -` diff --git a/src/Components/CollectionDescription.js b/src/Components/CollectionDescription.js index eacf37f7400594f2be78d06b24baecd69a928842..7d2b34d6d3bb50715095623bcfc445bc0ac61d67 100644 --- a/src/Components/CollectionDescription.js +++ b/src/Components/CollectionDescription.js @@ -16,19 +16,26 @@ 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, { useContext } from 'react'; +import React, { useContext, useEffect } from 'react'; +import axios from 'axios'; import { Store } from '../Store.js' import { Grid } from '@material-ui/core'; import Button from '@material-ui/core/Button'; import GetAppIcon from '@material-ui/icons/GetApp'; import AddIcon from '@material-ui/icons/Add'; import styled from 'styled-components'; +import LinearProgress from '@material-ui/core/LinearProgress'; import CollectionReview from './CollectionReview.js'; import FollowCollectionButton from './FollowCollectionButton.js'; +import { apiUrl } from '../env'; export default function CollectionDescription(props) { const { state } = useContext(Store); - console.log(state.currentUser); + + const handleDownloadCollection = () => { + axios.get(apiUrl+'/collections/'+props.collection_id+'/download'); + } + return ( <Grid container direction="column" justify="center" alignItems="center"> <Grid item> @@ -38,7 +45,9 @@ export default function CollectionDescription(props) { direction="row" justify="space-between" alignItems="center" > <Grid item sm={4}> - <CollectionReview/> + <CollectionReview + scrollToComment={props.scrollToComments} + id={props.collection_id}/> </Grid> <Grid item container sm={8} direction="column" justify="center" alignItems="flex-end" @@ -49,6 +58,7 @@ export default function CollectionDescription(props) { color="primary" startIcon={<GetAppIcon fontSize="large"/>} size="large" + onClick={handleDownloadCollection} > <ButtonText>Baixar Coleção</ButtonText> </DownloadButton> diff --git a/src/Components/CollectionReview.js b/src/Components/CollectionReview.js index 83487d0d1bf3cdd546f8ae9b8f5e1eb74d210f5e..4768ea306743d021d96fd31e129f42fa6f250a20 100644 --- a/src/Components/CollectionReview.js +++ b/src/Components/CollectionReview.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 } from 'react'; +import React, { useState, useEffect } from 'react'; import { Grid } from '@material-ui/core'; import styled from 'styled-components'; import Rating from '@material-ui/lab/Rating'; @@ -24,13 +24,38 @@ import IconButton from '@material-ui/core/IconButton'; import StarBorderIcon from '@material-ui/icons/StarBorder'; import FavoriteIcon from '@material-ui/icons/Favorite'; import InfoIcon from '@material-ui/icons/Info'; +import axios from 'axios'; +import { apiUrl } from '../env'; +import ReportModal from './ReportModal.js'; -export default function CollectionReview() { - //TODO: axios aqui +export default function CollectionReview(props) { //TODO: chamar o modal de report const likes = 2; const [liked, setLiked] = useState(false); const [stars, setStars] = useState(0); + const [reportOpen, setReportOpen] = useState(false); + + const handleClickReport = () => { + setReportOpen(true); + } + + const handleLikeClick = () => { + const url = apiUrl+'/collections/'+props.id+'/like'; + if (liked) + axios.put(url); + else + axios.delete(url); + setLiked(!liked); + } + + const handleSetStars = (value) => { + setStars(value); + props.scrollToComment(); + } + + const handleCloseModal = () => { + setReportOpen(false); + } return ( <Grid container direction="row" justify="center" alignItems="center"> @@ -38,22 +63,33 @@ export default function CollectionReview() { <Rating name="customized-empty" value={stars} - onChange={(e, value) => {setStars(value)}} + onChange={(e, value) => handleSetStars(value)} precision={0.5} style={{color:"#666"}} emptyIcon={<StarBorderIcon fontSize="inherit" />} /> </Grid> <Grid item sm={4}> - <IconButton aria-label="like" onClick={() => setLiked(!liked)}> + <IconButton aria-label="like" onClick={handleLikeClick}> {likes + (liked ? 1 : 0)}<FavoriteIcon /> </IconButton> </Grid> <Grid item sm={12}> - <IconButton aria-label="report" style={{fontSize: 'small'}}> + <IconButton + aria-label="report" + style={{fontSize: 'small'}} + onClick={handleClickReport}> <InfoIcon />Reportar erro ou abuso </IconButton> + <ReportModal + open={reportOpen} + handleClose={handleCloseModal} + form="colecao" + complainableId={props.id} + complainableType="Collection" + /> </Grid> + </Grid> ); } diff --git a/src/Components/Comment.js b/src/Components/Comment.js new file mode 100644 index 0000000000000000000000000000000000000000..e1a69e4d785d2edec29baf4c4612b56eeda776b5 --- /dev/null +++ b/src/Components/Comment.js @@ -0,0 +1,330 @@ +/*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, {useState, useContext, useEffect} from 'react' +import {Store} from '../Store.js' +import styled from 'styled-components' +import Grid from '@material-ui/core/Grid'; +import { Button } from '@material-ui/core'; +import {Link} from 'react-router-dom' +import {apiDomain} from '../env'; +import noAvatar from "../img/default_profile.png"; +import Rating from '@material-ui/lab/Rating'; +import StarBorderIcon from '@material-ui/icons/StarBorder'; +import EditIcon from '@material-ui/icons/Edit'; +import TextField from "@material-ui/core/TextField"; +import axios from 'axios' +import {apiUrl} from '../env'; +import Menu from '@material-ui/core/Menu'; +import MenuItem from '@material-ui/core/MenuItem'; +import ModalExcluir from './ModalExcluirComentario.js' + +export default function Comment (props) { + + /* + Required props: + rerenderCallback = callback function to trigger re-render on parent component + reviewRatings = required to update comment even though the user cannot update their rating score... + objectID = collection/learning object id + reviewID = self-explanatory I hope + authorID = author id; links to public user page + authorAvatar = either a string denoting the author's avatar file location or null + rating = star rating + name = title (?) + authorName = author username + description = the user comment itself + createdAt + recurso : boolean; determines whether to display orange or purple font + */ + var moment = require('moment') + + const {state} = useContext(Store) + const [displayedComment, setDisplayedComment] = useState(props.description) + const [editando, setEditando] = useState(false) + const [anchorEl, setAnchorEl] = React.useState(null); + const handleClick = (event) => { + setAnchorEl(event.currentTarget); + }; + const handleClose = () => { + setAnchorEl(null); + }; + const [modalOpen, toggleModal] = useState(false) + + const [comment, setComment] = useState({ + error : false, + value : props.description + }) + const handleChange = (e) => { + const userInput = e.target.value + const flag = (userInput.length === 0 ? true : false); + setComment({...comment, error : flag, value : userInput}) + } + + const updateComment = () => { + const finalComment = comment + if (!finalComment.error) { + let config = { + headers : { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Access-Token': sessionStorage.getItem('@portalmec/accessToken'), + 'Client': sessionStorage.getItem('@portalmec/clientToken'), + 'Uid': sessionStorage.getItem('@portalmec/uid'), + } + } + let payload = { + "review" : { + "name":null, + "description":finalComment.value, + "pros":null, + "cons":null, + "review_ratings_attributes" : props.reviewRatings + } + } + + axios.put( (`${apiUrl}/learning_objects/` + props.objectID + '/reviews/' + props.reviewID),payload, config + ).then((response) => { + if ( response.headers['access-token'] ) { + sessionStorage.setItem('@portalmec/accessToken', response.headers['access-token']) + } + console.log(response) + setDisplayedComment(finalComment.value) + setEditando(false) + props.handleSnackbar(2) + }, (error) => {console.log(error)}) + } + } + + const deleteComment = () => { + let config = { + headers : { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Access-Token': sessionStorage.getItem('@portalmec/accessToken'), + 'Client': sessionStorage.getItem('@portalmec/clientToken'), + 'Uid': sessionStorage.getItem('@portalmec/uid'), + } + } + axios.delete( (`${apiUrl}/learning_objects/` + props.objectID + '/reviews/' + props.reviewID), config).then((response) => { + if ( response.headers['access-token'] ) { + sessionStorage.setItem('@portalmec/accessToken', response.headers['access-token']) + } + props.rerenderCallback(); props.handleSnackbar(3)}, (error) => {console.log(error)}) + + toggleModal(false) + + } + + 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> + + { + 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> + <StyledButton + style={props.recurso ? {backgroundColor : "#ff7f00"} : {backgroundColor : "#673ab7"}} + onClick={() => updateComment()} + > + 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 && + <span className="date"> + {moment(props.createdAt).format("DD/MM/YYYY")} + </span> + } + </React.Fragment> + ) + } + </div> + + </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> + ) +} + +const StyledTextField = styled(TextField)` + label.Mui-focused { + color : ${props => props.colecao ? "rgb(103,58,183)" : "rgb(255,127,0)"}; + } + + .MuiInput-underline::after { + border-bottom: ${props => props.colecao ? "2px solid rgb(103,58,183)" : "2px solid rgb(255,127,0)"}; + } + +` + +const StyledDiv = styled.div` + text-align : center; + .MuiButton-root { + @media screen and (max-width: 990px) { + padding-right : 35px !important; + } + } +` +const StyledButton = styled(Button)` + color : rgba(255,255,255,0.87) !important; + box-shadow : 0 2px 5px 0 rgba(0,0,0,.26) !important; + margin : 6px 8px !important; + font-weight : 600 !important; +` + +const Comentario = styled.div` + @media screen and (max-width: 990px) { + padding-left : 55px !important; + } + font-size : 14px; + + .star-rating-container { + width : 100px; + } + + p { + margin : 0 0 10px; + padding-left : 2px + } + + a { + text-decoration : none !important; + } + + .date { + color : #ababab; + font-size : 12px; + font-weight : lighter; + padding-left : 3px; + } +` + +const AvatarDiv = styled.div` + text-align : center; + float : left; + position : relative; + width : 65px; + height : 65px; + a { + text-decoration : none !important; + } + + img { + width : 100% !important; + height : 100% !important; + border-radius : 100% + vertical-align : middle; + } +` + diff --git a/src/Components/FollowCollectionButton.js b/src/Components/FollowCollectionButton.js index 3956ede486c849017f13697b0f084f18146b3263..185a17f177fda0597157a6bf8d3dee3a0ef46743 100644 --- a/src/Components/FollowCollectionButton.js +++ b/src/Components/FollowCollectionButton.js @@ -65,39 +65,49 @@ export default function FollowCollectionButton(props) { setVariant("contained"); setButtonText("Seguindo"); setIcon(<CheckIcon fontSize="large"/>) - axios.post(apiUrl+'/Collection/'+props.collection_id+'/follow') + setFollowing(true); + axios.post(apiUrl+'/collections/'+props.collection_id+'/follow') .then(res => { - if (Number(res.status) == 201) - setFollowing(true); + if (Number(res.status) != 201) + setFollowing(false); + setIcon(<AddIcon fontSize="large"/>); + setButtonText("Seguindo"); + setVariant("contained"); }); } else { setVariant("outlined"); setButtonText("Deixar de seguir"); setIcon(<AddIcon fontSize="large"/>); - axios.delete(apiUrl+'/Collection/'+props.collection_id+'/follow') + setFollowing(false); + axios.delete(apiUrl+'/collections/'+props.collection_id+'/follow') .then(res => { if (Number(res.status) == 200) - setFollowing(false); + setFollowing(true); + setIcon(<CheckIcon fontSize="large"/>); + setButtonText("Deixar de seguir"); + setVariant("outlined"); }); } }; - return ( - <div> - <FollowButton - variant={variant} - color="primary" - startIcon={icon} - size="large" - onMouseEnter={handleMouseEnter} - onMouseLeave={handleMouseLeave} - onClick={handleClick} - > - <ButtonText>{button_text}</ButtonText> - </FollowButton> - <SignUpModal open={sign_up_open} handleClose={() => setSignUpOpen(false)} /> - </div> - ); + if (props.user_is_owner) + return ( + <div> + <FollowButton + variant={variant} + color="primary" + startIcon={icon} + size="large" + onMouseEnter={handleMouseEnter} + onMouseLeave={handleMouseLeave} + onClick={handleClick} + > + <ButtonText>{button_text}</ButtonText> + </FollowButton> + <SignUpModal open={sign_up_open} handleClose={() => setSignUpOpen(false)} /> + </div> + ); + else return (<div></div>); } const Title=styled.h1` diff --git a/src/Components/ReportCollectionForm.js b/src/Components/ReportCollectionForm.js new file mode 100644 index 0000000000000000000000000000000000000000..0832b014c1a6bd9314eccbce7fd712918f33ed26 --- /dev/null +++ b/src/Components/ReportCollectionForm.js @@ -0,0 +1,104 @@ +/*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, {useContext} from 'react'; +import { Button } from '@material-ui/core'; +import styled from 'styled-components' +import Radio from '@material-ui/core/Radio'; +import RadioGroup from '@material-ui/core/RadioGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import FormControl from '@material-ui/core/FormControl'; +import TextField from '@material-ui/core/TextField'; +import {StyledFormControl, StyledTextField, ButtonsDiv, ButtonCancelar, ButtonEnviar} from './ReportUserForm.js' + +export default function ReportRecursoForm (props) { + const [value, setValue] = React.useState(-1); + + const handleChange = (event) => { + setValue(event.target.value); + }; + + /*values are set according to backend complaint id*/ + const [options] = React.useState([ + {value : "1", text : 'A Coleção viola os direitos autorais.'}, + {value : "2", text : 'A Coleção contém conteúdo ofensivo/abusivo.'}, + {value : "5", text : 'A descrição da Coleção não corresponde ao seu conteúdo.'} + ]) + + const [moreInfo, setMoreInfo] = React.useState({ + key : false, + value : "", + }) + + const handleChangeMoreInfo = (e) => { + const userInput = e.target.value + + const flag = userInput.length > 150 ? true : false + + setMoreInfo({...moreInfo, + key : flag, + value : userInput + }) + } + + const handleSubmit = (e) => { + e.preventDefault() + const finalRadioValue = value + const finalMoreInfo = moreInfo + + if( finalRadioValue != -1 && !(finalMoreInfo.key)) { + props.handleSubmit(finalRadioValue, finalMoreInfo.value) + } + else { + console.log('oops') + } + } + + return ( + <form onSubmit={(e) => handleSubmit(e)} style={{textAlign : "left"}}> + <StyledFormControl component="fieldset"> + <RadioGroup value={value} onChange={handleChange}> + { + options.map(option => + <FormControlLabel value={option.value} control={<Radio color="orange"/>} label={option.text} /> + ) + } + </RadioGroup> + </StyledFormControl> + + <StyledTextField + id = {"report-text-field"} + label={"Escreva mais sobre o problema"} + value = {moreInfo.value} + onChange = {e => handleChangeMoreInfo(e)} + helperText = {moreInfo.value.length + '/150'} + multiline={true} + rowsMax = {"5"} + error = {moreInfo.key} + required = {false} + helperText ={moreInfo.value.length + '/150'} + style={{width : "100%"}} + /> + + <ButtonsDiv> + <ButtonCancelar onClick={props.handleClose}>CANCELAR</ButtonCancelar> + <ButtonEnviar type="submit">ENVIAR</ButtonEnviar> + </ButtonsDiv> + </form> + ) +} + diff --git a/src/Components/ReportModal.js b/src/Components/ReportModal.js index 5843515ef66f7226201576bbf327af6563c8ddbf..bb54f0908d48bbeda0a614b5befb577bbdd94bd2 100644 --- a/src/Components/ReportModal.js +++ b/src/Components/ReportModal.js @@ -27,7 +27,7 @@ import {Store} from '../Store.js' import axios from 'axios' import {apiUrl} from '../env'; import CloseIcon from '@material-ui/icons/Close'; -import ReportForm from './ReportForm.js' +import ReportCollectionForm from './ReportCollectionForm.js'; function CloseModalButton (props) { return ( @@ -69,6 +69,19 @@ export default function ReportModal (props) { */} } + const renderForm = (formType) => { + switch (formType) { + case 'colecao': + return ( + <ReportCollectionForm + handleClose={props.handleClose} + handleSubmit={handleSubmit} + /> + ); + break; + } + } + return ( <StyledModal aria-labelledby="transition-modal-title" @@ -92,7 +105,9 @@ export default function ReportModal (props) { </Header> <Content> - <ReportForm handleClose={props.handleClose} handleSubmit={handleSubmit}/> + { + renderForm(props.form) + } </Content> </ReportContainer> </Fade> @@ -118,7 +133,7 @@ const Header = styled.div` h2 { font-size : 26px; font-weight : lighter; - + color : #666 } ` @@ -163,3 +178,4 @@ const ReportContainer = styled.div` height : 100%; } ` + diff --git a/src/Components/ReportUserForm.js b/src/Components/ReportUserForm.js new file mode 100644 index 0000000000000000000000000000000000000000..1a906691d3b9ea6caca05448c9b5a2365178d352 --- /dev/null +++ b/src/Components/ReportUserForm.js @@ -0,0 +1,179 @@ +/*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, {useState, useContext} from 'react'; +import { Button } from '@material-ui/core'; +import styled from 'styled-components' +import Radio from '@material-ui/core/Radio'; +import RadioGroup from '@material-ui/core/RadioGroup'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; +import FormControl from '@material-ui/core/FormControl'; +import TextField from '@material-ui/core/TextField'; + +export default function ReportForm (props) { + const [value, setValue] = React.useState(-1); + + const handleChange = (event) => { + setValue(event.target.value); + }; + + /*values are set according to backend complaint id*/ + const [options] = React.useState([ + {value : "6", text :'Esta pessoa está fingindo ser eu ou alguém que eu conheço.'}, + {value : "4", text : 'Essa pessoa envia spam.'}, + {value : "3", text : 'Essa é uma conta falsa.'}, + {value : "1", text : ' A publicação dessa pessoa viola os diretos autorais.'}, + {value : "2", text : 'As publicações dessa pessoa contém conteúdo ofensivo/abusivo.'} + ]) + + const [moreInfo, setMoreInfo] = React.useState({ + key : false, + value : "", + }) + + const handleChangeMoreInfo = (e) => { + const userInput = e.target.value + + const flag = userInput.length > 150 ? true : false + + setMoreInfo({...moreInfo, + key : flag, + value : userInput + }) + } + + const formSubmit = (e) => { + e.preventDefault() + const finalRadioValue = value + const finalMoreInfo = moreInfo + + if( finalRadioValue != -1 && !(finalMoreInfo.key)) { + props.handleSubmit(finalRadioValue, finalMoreInfo.value) + } + } + + return ( + <form onSubmit={(e) => {formSubmit(e)}}> + <StyledFormControl component="fieldset"> + <RadioGroup value={value} onChange={handleChange}> + { + options.map(option => + <FormControlLabel value={option.value} control={<Radio color="orange"/>} label={option.text} /> + ) + } + </RadioGroup> + </StyledFormControl> + + <StyledTextField + id = {"Escreva mais sobre o problema"} + label={"Escreva mais sobre o problema"} + type = {"text"} + value = {moreInfo.value} + onChange = {e => handleChangeMoreInfo(e)} + helperText = {moreInfo.value.length + '/150'} + multiline={true} + rowsMax = {"5"} + error = {moreInfo.key} + required = {false} + style={{width:"100%"}} + /> + + <ButtonsDiv> + <ButtonCancelar onClick={props.handleClose}>CANCELAR</ButtonCancelar> + <ButtonEnviar type="submit">ENVIAR</ButtonEnviar> + </ButtonsDiv> + </form> + ); +} + +export const ButtonsDiv = styled.div` + display : flex; + flex-direction : row; + justify-content : flex-end; + align-items : center; +` + +export const ButtonCancelar = styled(Button)` + &:hover { + background-color : rgba(158,158,158,0.2) !important; + } + height : 36px !important; + padding-left : 16px !important; + padding-right : 16px !important; + font-weight : 500 !important; + border-radius : 3px !important; + color :#666 !important; + background-color: transparent; + min-width : 88px !important; + height : 36px !important; +` + +export const ButtonEnviar = styled(Button)` + background-color : #ff7f00 !important; + color : #fff !important; + font-size: 14px !important; + font-weight: 500 !important; + height: 36px !important; + border-radius: 3px !important; + padding-left: 16px !important; + padding-right: 16px !important; + box-shadow: 0 2px 5px 0 rgba(0,0,0,.26) !important; + outline : none !important; + min-width : 88px !important; + vertical-align : middle !important; + margin : 6px 8px !important; + text-decoration : none !important; + + .MuiButton-label { + padding-right : 16px; + padding-left : 16px; + } +` +export const StyledTextField = styled(TextField)` + .MuiFormHelperText-root { + text-align : right; + } + + label.Mui-focused { + color : orange; + } + + label.Mui-focused.Mui-error { + color : red; + } + + .MuiInput-underline::after { + border-bottom: 2px solid orange; + } +` + +export const StyledFormControl = styled(FormControl)` + .MuiFormControlLabel-root { + color : #666; + } + .MuiIconButton-label { + color : #666; + } + .PrivateRadioButtonIcon-checked { + color : orange; + } + + .MuiTypography-body1 { + font-size : 14px; + } +` + diff --git a/src/Components/ResourceList.js b/src/Components/ResourceList.js index 5c5230fb954045f2a0e66c90256fcad5f5b3d29a..d325074daf1e760277201527c43fe27eb6dddf6c 100644 --- a/src/Components/ResourceList.js +++ b/src/Components/ResourceList.js @@ -24,6 +24,7 @@ import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank'; import CheckBoxIcon from '@material-ui/icons/CheckBox'; import GetAppIcon from '@material-ui/icons/GetApp'; import ResourceCardFunction from './ResourceCardFunction.js'; +import CircularProgress from '@material-ui/core/CircularProgress'; export default function ResourceList(props) { @@ -44,11 +45,19 @@ export default function ResourceList(props) { return <CheckBoxOutlineBlankIcon />; } + const handleDownloadSelection = () => { + const selected_resources = props.resources.filter(resource => selected[props.resources.indexOf(resource)]); + } + return ( <ResourceListContainer> - <Grid container direction="row" justify="space-between" alignItems="center"> + <Grid container direction="row" justify="space-around" alignItems="center"> <Grid item> - <Title>{props.resources.length} recurso{props.resources.length == 1 ? "" : "s"}</Title> + <Title> + {props.resources.length ? + props.resources.length+" recurso"+(props.resources.length == 1 ? "" : "s") + : "Carregando coleção"} + </Title> </Grid> <Grid item> <Button color="primary" onClick={() => setSelectable(!selectable)}> @@ -62,15 +71,16 @@ export default function ResourceList(props) { color="primary" variant="outlined" startIcon={<GetAppIcon fontSize="large"/>} + onClick={handleDownloadSelection} > <PanelButtonText>baixar seleção</PanelButtonText> </Button> </Grid> </Grid> - <Grid container direction="row" justify="space-around" alignItems="center"> + <Grid container direction="row" justify="flex-start" alignItems="center"> {props.resources.map((r) => { return ( - <Grid item key={r.title}> + <ResourceGrid item key={r.title}> <ResourceCardFunction type={r.type} author={r.author} @@ -94,7 +104,7 @@ export default function ResourceList(props) { : <span></span>} - </Grid> + </ResourceGrid> ); })} </Grid> @@ -117,3 +127,6 @@ const SelectButton=styled(Button)` const PanelButtonText=styled.span` font-weight: 900; ` +const ResourceGrid=styled(Grid)` + padding-right: 7px; +` diff --git a/src/Pages/CollectionPage.js b/src/Pages/CollectionPage.js index 6c8074ead92a2acbd63813bb0a8ac51a5c6790e3..2e789a4ba12b5a3547b571868c2484721bd3087b 100644 --- a/src/Pages/CollectionPage.js +++ b/src/Pages/CollectionPage.js @@ -16,15 +16,37 @@ 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 React, { useRef, useState, useEffect } from 'react'; +import axios from 'axios'; import { Grid } from '@material-ui/core'; import CollectionAuthor from '../Components/CollectionAuthor.js'; import VerticalRuler from '../Components/VerticalRuler.js'; import CollectionDescription from '../Components/CollectionDescription.js'; import ResourceList from '../Components/ResourceList.js'; import CollectionCommentSection from '../Components/CollectionCommentSection.js'; +import { apiUrl, apiDomain } from '../env'; +import CircularProgress from '@material-ui/core/CircularProgress'; + +export default function CollectionPage(props) { + const [collection, setCollection] = useState({ + name: '', + id: 0, + }); + const collection_id = props.match.params.id; + const comment_ref = useRef(null); + + useEffect(()=>{ + axios.get(apiUrl+'/collections/'+collection_id) + .then(res => { + setCollection(Object.assign({}, res.data)); + console.log(res.data); + }); + }, []); + + const handleScrollToComments = () => { + window.scrollTo(0, comment_ref.current.offsetTop); + } -export default function CollectionPage() { return ( <Grid container direction="row" @@ -33,33 +55,43 @@ export default function CollectionPage() { style={mainContainerStyle}> <Grid item xs={10} md={3}> - <CollectionAuthor name="Glaucia Gomes" imgsrc="https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fcdn2.iconfinder.com%2Fdata%2Ficons%2Fsocial-flat-buttons-3%2F512%2Fduckduckgo-512.png&f=1&nofb=1"/> + <CollectionAuthor + name={collection.owner ? collection.owner.name : ""} + imgsrc={collection.owner ? apiDomain+collection.owner.avatar : ''}/> </Grid> <VerticalRuler width={1} height={300} color="rgb(238, 238, 238)"/> <Grid item xs={10} md={6}> - <CollectionDescription title="Linguagem de Programacao" collection_id="9868"/> + <CollectionDescription + scrollToComments={handleScrollToComments} + title={collection.name ? collection.name : ""} + collection_id={collection.id ? collection.id : 0}/> </Grid> <Grid container item direction="row" justify="center" alignItems="center" style={{backgroundColor: '#f4f4f4'}}> <Grid item xs={10}> - <ResourceList resources={[{ - type: "Outros", - author: "Luciano Hulk", - tags: ["Recursos_do_luciano", "Matemática"], - published: true, - title: "Caldeirão do Pitágoras", - rating: 0.5, - likeCount: 8, - liked: false, - avatar: null, - thumbnail: null - }]} - selectable={true} - /> + <ResourceList resources={ + collection.collection_items ? + collection.collection_items.map(i => { + return { + type: i.collectionable.object_type, + author: i.collectionable.author, + published: i.collectionable.published_at, + title: i.collectionable.name, + rating: i.collectionable.review_average, + likeCount: i.collectionable.likes_count, + liked: i.collectionable.liked, + avatar: i.collectionable.publisher.avatar, + thumbnail: i.collectionable.thumbnail, + tags: i.collectionable.tags.map(t => t.name), + } + }) + : [] + }/> + </Grid> - <Grid item xs={10} style={{marginTop: 40}}> + <Grid item xs={10} style={{marginTop: 40}} ref={comment_ref}> <CollectionCommentSection /> </Grid> </Grid>