diff --git a/src/App.js b/src/App.js index f4dc1236f65c0a0444fa12c66e619a77bdc84e6d..b6bda2253d5c84dcebce13ac439c6c407cf8f56b 100644 --- a/src/App.js +++ b/src/App.js @@ -48,6 +48,7 @@ import UploadPage from './Pages/UploadPage.js' import EditLearningObjectPage from './Pages/EditLearningObjectPage.js' import SiteMap from './Pages/SiteMap' import Accessibility from './Pages/Accessibility' +import CollectionPage from './Pages/CollectionPage.js' export default function App(){ // eslint-disable-next-line @@ -112,6 +113,7 @@ export default function App(){ <Route path='/professor' component={PageProfessor}/> <Route path="/upload" component={UploadPage}/> <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 new file mode 100644 index 0000000000000000000000000000000000000000..54ce444032c4e4cefc778371b1e8f4144188a320 --- /dev/null +++ b/src/Components/CollectionAuthor.js @@ -0,0 +1,73 @@ +/*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 { Grid } from '@material-ui/core'; +import styled from 'styled-components'; +import { Link } from 'react-router-dom'; +import CircularProgress from '@material-ui/core/CircularProgress'; +import LinearProgress from '@material-ui/core/LinearProgress'; + + +export default function CollectionAuthor(props) { + return ( + <Grid container + direction="column" + justify="center" + alignItems="center"> + {props.imgsrc ? + <UserLink + to={`/usuario-publico/${props.author_id}`} + > + <UserAvatar src={props.imgsrc}/> + </UserLink> + : + <CircularProgress color="secondary"/> + } + <InfoText>Coleção organizada por:</InfoText> + {props.name ? + <UserLink to={`/usuario-publico/${props.author_id}`} > + <UserName>{props.name}</UserName> + </UserLink> + : + <CircularProgress /> + } + </Grid> + ); +} + +const UserAvatar = styled.img` + border-radius: 100px; + width: 100px; + height: auto; +` +const InfoText = styled.p` + margin-bottom: 0; + padding-bottom: 0; + color: #666; +` +const UserName = styled.h1` + margin-top: 10px; + color: #673ab7; +` +const UserLink = styled(Link)` + text-decoration: none; + &:focus, &:hover, &:visited, &:link, &:active { + text-decoration: none; + } +` diff --git a/src/Components/CollectionCommentSection.js b/src/Components/CollectionCommentSection.js new file mode 100644 index 0000000000000000000000000000000000000000..df6ead8183a7b462c0f588d6415bccd9317b2255 --- /dev/null +++ b/src/Components/CollectionCommentSection.js @@ -0,0 +1,170 @@ +/*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, { useRef, useState, useEffect } from 'react'; +import { Grid } from '@material-ui/core'; +import Card from '@material-ui/core/Card'; +import Button from '@material-ui/core/Button'; +import EditIcon from '@material-ui/icons/Edit'; +import styled from 'styled-components'; +import axios from 'axios'; +import { apiUrl } from '../env'; +import CommentForm from './ResourcePageComponents/CommentForm.js'; +import Comment from './Comment.js'; +import Snackbar from '@material-ui/core/Snackbar'; +import MuiAlert from '@material-ui/lab/Alert'; +import Comentario from '../img/comentarios.png'; + +export default function CollectionCommentSection(props) { + const [post_snack_open, setPostSnackOpen] = useState(false); + const [delete_snack_open, setDeleteSnackOpen] = useState(false); + const [render_state, setRenderState] = useState(false); + const [reviews, setReviews] = useState([]); + const comment_ref = useRef(null); + + const forceUpdate = () => { setRenderState(!render_state); } + + const handlePostSnackbar = () => { + setPostSnackOpen(!post_snack_open); + } + + const handleDeleteSnackbar = () => { + setDeleteSnackOpen(!delete_snack_open); + } + + const handleScrollToCommentForm = () => { + window.scrollTo(0, comment_ref.current.offsetTop); + } + + function Alert(props) { + return <MuiAlert elevation={6} variant="filled" {...props} />; + } + + const NoCommentsMessage = () => { + const NoCommentsContainer=styled.div` + text-align: center; + margin-left: 9vw; + margin-right: 9vw; + ` + const BlueTitle=styled.h2` + color: #673ab7; + ` + const Secondary=styled.h3` + font-weight: 100; + ` + const ButtonText=styled.span` + font-weight: 900; + ` + const Image=styled.img` + ` + return ( + <NoCommentsContainer> + <Image src={Comentario} /> + <BlueTitle>Compartilhe sua opinião com a rede!</BlueTitle> + <Secondary>Gostou desta coleção? Comente e compartilhe com a rede sua opinião. Interagindo com a rede, você contribui para que mais coleções como esta sejam criadas.</Secondary> + <Button + variant="contained" + color="primary" + startIcon={<EditIcon />} + onClick={handleScrollToCommentForm} + > + <ButtonText>Relatar experiência</ButtonText> + </Button> + </NoCommentsContainer> + ); + } + const CollectionComments = () => { + return ( + <div> + <Title>{reviews.length} {reviews.length == 1 ? "Relato" : "Relatos"} sobre a Coleção</Title> + {reviews.map(r => { + return ( + <Comment + 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} + recurso={false} + /> + );})} + </div> + ); + } + + useEffect(() => { + axios.get(apiUrl+'/collections/'+props.id+'/reviews') + .then(res => { + setReviews(res.data); + }); + }, [render_state]); + + return ( + <CommentAreaContainer container xs={12} direction="row" justify="center" alignItems="center"> + <Grid item xs={12} ref={comment_ref}> + <CommentAreaCard> + <Title>Conte sua experiência com a coleção</Title> + <CommentForm colecao recursoId={props.id} + handleSnackbar={handlePostSnackbar} + rerenderCallback={forceUpdate} + /> + {reviews.length ? CollectionComments() : NoCommentsMessage()} + </CommentAreaCard> + </Grid> + <Snackbar + open={post_snack_open} + autoHideDuration={6000} + onClose={handlePostSnackbar} + anchorOrigin={{vertical: 'top', horizontal: 'right'}} + > + <Alert onClose={handlePostSnackbar} severity="info"> + Seu comentário foi publicado com sucesso! + </Alert> + </Snackbar> + <Snackbar + open={delete_snack_open} + autoHideDuration={6000} + onClose={handleDeleteSnackbar} + anchorOrigin={{vertical: 'top', horizontal: 'right'}} + > + <Alert onClose={handleDeleteSnackbar} severity="info"> + Comentário deletado com sucesso. + </Alert> + </Snackbar> + </CommentAreaContainer> + ); +} + + +const CommentAreaContainer=styled(Grid)` + margin-left: 10%; + margin-right: 10%; +` +const CommentAreaCard=styled(Card)` + padding: 45px; +` +const Title=styled.h1` + font-weight: 100; + color: #666; +` diff --git a/src/Components/CollectionDescription.js b/src/Components/CollectionDescription.js new file mode 100644 index 0000000000000000000000000000000000000000..062a13bca5ba4b24c3eee0b3889131eda749b209 --- /dev/null +++ b/src/Components/CollectionDescription.js @@ -0,0 +1,103 @@ +/*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, useState, 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, apiDomain } from '../env'; + +export default function CollectionDescription(props) { + const { state } = useContext(Store); + const [download_url, setDownloadUrl] = useState(''); + + useEffect(() => { + const body = { + "package": { + "object": [{"type": "Collection", "id": props.collection_id}] + }}; + axios + .post(apiUrl+'/package', body) + .catch(err => { + if (err.response && err.response.status === 302) { + setDownloadUrl(apiDomain+'/'+err.response.data.url); + } + });}, [props.collection_id]); + + return ( + <Grid container direction="column" justify="center" alignItems="center"> + <Grid item> + <Title>{props.title}</Title> + </Grid> + <Grid item container + direction="row" justify="space-between" alignItems="center" + > + <Grid item sm={4}> + <CollectionReview + scrollToComment={props.scrollToComments} + id={props.collection_id}/> + </Grid> + <Grid item container sm={8} + direction="column" justify="center" alignItems="flex-end" + > + <Grid item style={{marginBottom: 10}}> + <DownloadAnchor href={download_url} > + <DownloadButton + variant="outlined" + color="primary" + startIcon={<GetAppIcon fontSize="large"/>} + size="large" + > + <ButtonText>Baixar Coleção</ButtonText> + </DownloadButton> + </DownloadAnchor> + </Grid> + <Grid item> + <FollowCollectionButton user_id={state.currentUser.id} collection_id={props.collection_id}/> + </Grid> + </Grid> + </Grid> + + </Grid> + ); +} + +const Title=styled.h1` + font-size: 3em; + color: rgb(102, 102, 102); + float: left; +` +const ButtonText=styled.span` + font-weight: bolder; + font-size: 1.2em; +` +const DownloadButton=styled(Button)` + padding-left: 10; + padding-right: 10; + width: 250px; +` +const DownloadAnchor=styled.a` + text-decoration: none !important; +` diff --git a/src/Components/CollectionReview.js b/src/Components/CollectionReview.js new file mode 100644 index 0000000000000000000000000000000000000000..be63b1d3a1a81e92d4358956a3461ea6302f3c9d --- /dev/null +++ b/src/Components/CollectionReview.js @@ -0,0 +1,120 @@ +/*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, useState, useEffect } from 'react'; +import { Grid } from '@material-ui/core'; +import styled from 'styled-components'; +import Rating from '@material-ui/lab/Rating'; +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 { Store } from '../Store.js' +import ReportModal from './ReportModal.js'; +import SignUpModal from './SignUpModal.js'; +import LoginModal from './LoginModal.js'; + +export default function CollectionReview(props) { + const [likes, setLikes] = useState(0); + const [liked, setLiked] = useState(false); + const [stars, setStars] = useState(0); + const [reportOpen, setReportOpen] = useState(false); + const [sign_up_open, setSignUpOpen] = useState(false); + const [log_in_open, setLoginOpen] = useState(false); + const { state } = useContext(Store); + + useEffect(() => { + axios.get(apiUrl+'/collections/'+props.id) + .then(res => { + setLikes(Number(res.data.likes_count)); + setLiked(res.data.liked); + }); + }, [props.id]); + + const handleClickReport = () => { + setReportOpen(true); + } + + const handleLikeClick = () => { + if (state.currentUser.id) { + const url = apiUrl+'/collections/'+props.id+'/like'; + if (!liked) + axios.put(url); + else + axios.delete(url); + setLiked(!liked); + } else + setSignUpOpen(true); + } + + const handleSetStars = (value) => { + setStars(value); + props.scrollToComment(); + } + + const handleCloseModal = () => { + setReportOpen(false); + } + + return ( + <Grid container direction="row" justify="center" alignItems="center"> + <Grid item sm={8}> + <Rating + name="customized-empty" + value={stars} + 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={handleLikeClick}> + {likes + (liked ? 1 : 0)}<FavoriteIcon /> + </IconButton> + </Grid> + <Grid item sm={12}> + <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> + <SignUpModal + open={sign_up_open} + handleClose={() => setSignUpOpen(false)} + openLogin={() => setLoginOpen(true)} + /> + <LoginModal + open={log_in_open} + handleClose={() => setLoginOpen(false)} + /> + + </Grid> + ); +} diff --git a/src/Components/Comment.js b/src/Components/Comment.js index 5c797282aeccccb928ede08e34d1c0e207830e3e..e1a69e4d785d2edec29baf4c4612b56eeda776b5 100644 --- a/src/Components/Comment.js +++ b/src/Components/Comment.js @@ -327,3 +327,4 @@ const AvatarDiv = styled.div` vertical-align : middle; } ` + diff --git a/src/Components/FloatingDownloadButton.js b/src/Components/FloatingDownloadButton.js new file mode 100644 index 0000000000000000000000000000000000000000..5d78b87d0f5eebc66b5fd63a478b13fa00147ccd --- /dev/null +++ b/src/Components/FloatingDownloadButton.js @@ -0,0 +1,72 @@ +/*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, useEffect } from 'react'; +import styled from 'styled-components'; +import axios from 'axios'; +import {apiUrl} from '../env'; +import GetAppIcon from '@material-ui/icons/GetApp'; +import Fab from '@material-ui/core/Fab'; +import Snackbar from '@material-ui/core/Snackbar'; +import MuiAlert from '@material-ui/lab/Alert'; + +function Alert(props) { + return <MuiAlert elevation={6} variant="filled" {...props} />; +} + +export default function FloatingDownloadButton (props) { + const [snackbar, setSnackbar] = useState(false); + + const handleClickDownload = () => { + if (props.empty_selection) + setSnackbar(true); + } + + const handleClose = () => { + setSnackbar(false); + } + + return ( + <div> + <DownloadAnchor href={props.url} alt="Baixar recursos selecionados"> + <FloatingDownload + color="primary" + aria-label="download" + onClick={handleClickDownload} + > + <GetAppIcon /> + </FloatingDownload> + </DownloadAnchor> + <Snackbar open={snackbar} autoHideDuration={6000} onClose={handleClose}> + <Alert onClose={handleClose} severity="alert"> + Selecione recursos para poder baixar + </Alert> + </Snackbar> + </div> + ); +} + +const DownloadAnchor=styled.a` + text-decoration: none !important; +` + +const FloatingDownload=styled(Fab)` + position: fixed !important; + right: 15px !important; + bottom: 25px !important; +` diff --git a/src/Components/FollowCollectionButton.js b/src/Components/FollowCollectionButton.js new file mode 100644 index 0000000000000000000000000000000000000000..5e3fd33c9ce3b789de2d15373e63e6858b937752 --- /dev/null +++ b/src/Components/FollowCollectionButton.js @@ -0,0 +1,126 @@ +/*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, useEffect } from 'react'; +import Button from '@material-ui/core/Button'; +import CheckIcon from '@material-ui/icons/Check'; +import AddIcon from '@material-ui/icons/Add'; +import styled from 'styled-components'; +import SignUpModal from './SignUpModal.js'; +import axios from 'axios'; +import {apiUrl} from '../env'; + +export default function FollowCollectionButton(props) { + const [icon, setIcon] = useState(<AddIcon fontSize="large"/>); + const [button_text, setButtonText] = useState("Seguir Coleção"); + const [variant, setVariant] = useState("outlined"); + const [sign_up_open, setSignUpOpen] = useState(false); + const [following, setFollowing] = useState(false); //user following collection + + useEffect(() => { + axios.get(apiUrl+'/users/'+props.user_id+'/following/Collection') + .then(res => { + for (const element in res.body) + if (element.id == props.collection_id) + setFollowing(true); + }); + }, []) + + //handleMouse{Enter, Leave} only do anything when user follows given collection: + const handleMouseEnter = () => { + if (following) { + setVariant("outlined"); + setButtonText("Deixar de seguir"); + setIcon(null); + } + } + + const handleMouseLeave = () => { + if (following) { + setVariant("contained"); + setButtonText("Seguindo"); + setIcon(<CheckIcon fontSize="large" />); + } + } + + const handleClick = () => { + if (!props.user_id) + setSignUpOpen(true); + else if (!following) { + setVariant("contained"); + setButtonText("Seguindo"); + setIcon(<CheckIcon fontSize="large"/>) + setFollowing(true); + axios.post(apiUrl+'/collections/'+props.collection_id+'/follow') + .then(res => { + 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"/>); + setFollowing(false); + axios.delete(apiUrl+'/collections/'+props.collection_id+'/follow') + .then(res => { + if (Number(res.status) == 200) + setFollowing(true); + setIcon(<CheckIcon fontSize="large"/>); + setButtonText("Deixar de seguir"); + setVariant("outlined"); + }); + } + }; + + 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` + font-size: 3em; + color: rgb(102, 102, 102); + float: left; +` +const ButtonText=styled.span` + font-weight: bolder; + font-size: 1.2em; +` +const FollowButton=styled(Button)` + padding-left: 10; + padding-right: 10; + width: 250px; +` diff --git a/src/Components/ModalExcluirComentario.js b/src/Components/ModalExcluirComentario.js index 88ceeceff8c5c798b2e1fe85872451a43178172e..4ce108a20896e848b2e01177694ad2a139efa843 100644 --- a/src/Components/ModalExcluirComentario.js +++ b/src/Components/ModalExcluirComentario.js @@ -69,7 +69,7 @@ export default function ModalExcluir (props) { aria-labelledby="transition-modal-title" aria-describedby="transition-modal-description" open={props.open} - + centered="true" onClose={props.handleClose} closeAfterTransition @@ -101,3 +101,4 @@ export default function ModalExcluir (props) { ) } + 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 ed472b3890cda0cbc7bd02e7aa6b1f44714e0131..540e1de9ae151b92d520e7b8d9105054c069d00b 100644 --- a/src/Components/ReportModal.js +++ b/src/Components/ReportModal.js @@ -30,6 +30,7 @@ import CloseIcon from '@material-ui/icons/Close'; import ReportUserForm from './ReportUserForm.js' import ReportRecursoForm from './ReportRecursoForm.js' import ReportColecaoForm from './ReportColecaoForm.js' +import ReportCollectionForm from './ReportCollectionForm.js'; function CloseModalButton (props) { return ( @@ -87,6 +88,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" @@ -183,3 +197,4 @@ const ReportContainer = styled.div` height : 100%; } ` + diff --git a/src/Components/ReportUserForm.js~fead909286087ce07b01b25f6d8f46f74dc494c8 b/src/Components/ReportUserForm.js~fead909286087ce07b01b25f6d8f46f74dc494c8 new file mode 100644 index 0000000000000000000000000000000000000000..1a906691d3b9ea6caca05448c9b5a2365178d352 --- /dev/null +++ b/src/Components/ReportUserForm.js~fead909286087ce07b01b25f6d8f46f74dc494c8 @@ -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 new file mode 100644 index 0000000000000000000000000000000000000000..08a13d469c617179ce26f739450741ccc9358eb4 --- /dev/null +++ b/src/Components/ResourceList.js @@ -0,0 +1,184 @@ +/*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, useEffect } from 'react'; +import { Grid } from '@material-ui/core'; +import styled from 'styled-components'; +import axios from 'axios'; +import Button from '@material-ui/core/Button'; +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 FloatingDownloadButton from './FloatingDownloadButton.js'; +import CircularProgress from '@material-ui/core/CircularProgress'; +import Snackbar from '@material-ui/core/Snackbar'; +import MuiAlert from '@material-ui/lab/Alert'; +import { apiUrl } from '../env.js'; + +function Alert(props) { + return <MuiAlert elevation={6} variant="filled" {...props} />; +} + + +export default function ResourceList(props) { + const [selected, setSelected] = useState(Array.apply( + null, {length: props.resources.length}).map(i => false)); + const [selectable, setSelectable] = useState(false); + const [download_url, setDownloadUrl] = useState(''); + const [snackbar_open, setSnackbarOpen] = useState(false); + + const updateSelected = (index) => { + let new_selected = selected.slice(); + new_selected[index] = !new_selected[index]; + setSelected(new_selected); + } + + const checkBoxIcon = (s) => { + if (s) + return <CheckBoxIcon />; + else + return <CheckBoxOutlineBlankIcon />; + } + + const handleDownloadSelection = () => { + const selected_resources = props.resources.filter(resource => selected[props.resources.indexOf(resource)]); + setSnackbarOpen(true); + } + + useEffect(() => { + if (selectable) { + var resources = [] + let i = 0; + while (i < props.resources.length) { + if (selected[i]) + resources.push({"type": "LearningObject", "id": props.resources[i].id}); + i++; + } + const body = { + "package": { + "object": resources + }}; + if (resources.length) + axios + .post(apiUrl+'/package', body) + .catch(err => { + if (err.response && err.response.status === 302) { + setDownloadUrl(apiUrl.slice().replace('/v1', '')+'/'+err.response.data.url); + } + }); + } + }, [selected, selectable]); + + return ( + <ResourceListContainer> + <Grid container direction="row" justify="space-around" alignItems="center"> + <Grid item> + <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)}> + <PanelButtonText> + {selectable ? "Desativar" : "Ativar"} seleção + </PanelButtonText> + </Button> + </Grid> + <Grid item> + <UnstyledAnchor href={download_url}> + <Button + color="primary" + variant="outlined" + startIcon={<GetAppIcon fontSize="large"/>} + onClick={handleDownloadSelection} + > + <PanelButtonText>baixar seleção</PanelButtonText> + </Button> + </UnstyledAnchor> + </Grid> + </Grid> + <Grid container direction="row" justify="flex-start" alignItems="center"> + {props.resources.map((r) => { + return ( + <ResourceGrid item key={r.title}> + <ResourceCardFunction + type={r.type} + author={r.author} + tags={r.tags} + published={r.published} + title={r.title} + rating={r.rating} + likeCount={r.likeCount} + liked={r.liked} + thumbnail={r.thumbnail} + /> + {selectable ? + (<SelectButton + variant="outline" + color="primary" + startIcon={checkBoxIcon(selected[props.resources.indexOf(r)])} + onClick={() => updateSelected(props.resources.indexOf(r))} + > + Selecionar + </SelectButton>) + : + <span></span>} + + </ResourceGrid> + ); + })} + </Grid> + <Snackbar + open={snackbar_open} + autoHideDuration={6000} + onClose={() => setSnackbarOpen(false)} + anchorOrigin={{vertical: 'top', horizontal: 'right'}} + > + <Alert onClose={() => setSnackbarOpen(false)} severity="info"> + Os recursos serão baixados + </Alert> + </Snackbar> + <FloatingDownloadButton url={download_url} empty={selected.indexOf(true) == -1}/> + </ResourceListContainer> + ); +} + +const ResourceListContainer=styled.div` + margin-left: 20; + margin-right: 20; +` +const Title=styled.p` + color: rgb(102, 102, 102); + font-size: 2em; + font-weigth: 300; +` +const SelectButton=styled(Button)` + width: 100%; +` +const PanelButtonText=styled.span` + font-weight: 900; +` +const ResourceGrid=styled(Grid)` + padding-right: 7px; +` +const UnstyledAnchor=styled.a` + text-decoration: none !important; +` diff --git a/src/Components/ResourcePageComponents/CommentForm.js b/src/Components/ResourcePageComponents/CommentForm.js index efbf0a0349ad1184ac616bd738faffa5abfba7a0..0b13b1479b9329bf1f31228575d829fb60f27f2a 100644 --- a/src/Components/ResourcePageComponents/CommentForm.js +++ b/src/Components/ResourcePageComponents/CommentForm.js @@ -72,8 +72,6 @@ export default function CommentForm (props) { } } - let windowWidth = window.innerWidth - return ( <StyledForm onSubmit={handleSubmit}> <label htmlFor="avaliacao-estrelas" className="start-label"> @@ -93,77 +91,35 @@ export default function CommentForm (props) { <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> - { - windowWidth > 990 ? - ( - <> - <Grid item xs={10}> - <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") : ''} - /> - </Grid> - <Grid item xs={2}> - <div style={{height : "100%", display : "flex", flexDirection : "column", justifyContent : "flex-end"}}> - { - props.recurso ? - ( - <OrangeButton type="submit">Publicar</OrangeButton> - ) - : - ( - <PurpleButton type="submit"><EditIcon/>Enviar</PurpleButton> - ) - } - </div> - </Grid> + <Grid item xs={10}> + <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") : ''} + /> + </Grid> + <Grid item xs={2}> + <div style={{height : "100%", display : "flex", flexDirection : "column", justifyContent : "flex-end"}}> + { + props.recurso ? + ( + <OrangeButton type="submit">Publicar</OrangeButton> + ) + : + ( + <PurpleButton type="submit"><EditIcon/>Enviar</PurpleButton> + ) + } + </div> + </Grid> - <div className="campos-obrigatorios">* Campos obrigatórios.</div> - </> - ) - : - ( - <> - <Grid item xs={12}> - <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") : ''} - /> - </Grid> - <Grid item xs={12}> - <div style={{paddingTop : "18px", display : "flex", justifyContent : "space-between"}}> - <div className="campos-obrigatorios">* Campos obrigatórios.</div> - <div style={{width : "40%"}}> - { - props.recurso ? - ( - <OrangeButton type="submit">Publicar</OrangeButton> - ) - : - ( - <PurpleButton type="submit"><EditIcon/>Enviar</PurpleButton> - ) - } - </div> - </div> - </Grid> - </> - ) - } + <div className="campos-obrigatorios">* Campos obrigatórios.</div> </Grid > </StyledForm> ) @@ -221,9 +177,6 @@ const StyledForm = styled.form` display : flex; flex-direction : column; text-align : start; - @media screen and (max-width : 990px) { - padding-left : 15px; - } .start-label { font-size : 14px; diff --git a/src/Components/ResourcePageComponents/CommentsArea.js b/src/Components/ResourcePageComponents/CommentsArea.js index 924a690434497aa0f7e133ff4ddec765310d5566..9a06e8a3648fc921845d22891f9e2bcc1c28c149 100644 --- a/src/Components/ResourcePageComponents/CommentsArea.js +++ b/src/Components/ResourcePageComponents/CommentsArea.js @@ -62,7 +62,7 @@ export default function CommentsArea (props) { recursoId={props.recursoId} handleSnackbar={props.handleSnackbar} rerenderCallback={forceUpdate} - recurso={props.recurso} + recurso={true} /> </Grid> </Grid> @@ -183,12 +183,18 @@ const GrayContainer = styled.div` display : flex; flex-direction : column; justify-content : space-between; +<<<<<<< HEAD font-size : 14px; padding-bottom : 20px; @media screen and (min-width : 990px) { padding-right : 15px; padding-left : 15px; } +======= + padding-right : 15px; + padding-left : 15px; + padding-bottom : 20px; +>>>>>>> fead909286087ce07b01b25f6d8f46f74dc494c8 h3 { font-family : 'Roboto Light','Roboto Regular',Roboto; diff --git a/src/Components/VerticalRuler.js b/src/Components/VerticalRuler.js new file mode 100644 index 0000000000000000000000000000000000000000..eb178b1336e30b671c5627b9ec4a76c487a52602 --- /dev/null +++ b/src/Components/VerticalRuler.js @@ -0,0 +1,30 @@ +/*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'; + + +export default function VerticalRuler(props) { + return ( + <div + style={{ + borderLeft: ''+props.width+'px solid '+props.color, + height:props.height + }}/> + ); +} diff --git a/src/Pages/CollectionPage.js b/src/Pages/CollectionPage.js index 53f2351c0eade585a0709d459f9f940299b60bb5..f7b3a32e0016fde9b1370e7f5d5024549a7b4ed5 100644 --- a/src/Pages/CollectionPage.js +++ b/src/Pages/CollectionPage.js @@ -2,7 +2,6 @@ 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 @@ -16,15 +15,90 @@ 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, {Component} 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)); + }); + }, []); + + const handleScrollToComments = () => { + window.scrollTo(0, comment_ref.current.offsetTop); + } + + return ( + <Grid container + direction="row" + justify="space-around" + alignItems="center" + style={mainContainerStyle}> + + <Grid item xs={10} md={3}> + <CollectionAuthor + author_id={collection.owner ? collection.owner.id : 0} + 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 + scrollToComments={handleScrollToComments} + title={collection.name ? collection.name : ""} + collection_id={collection.id ? collection.id : 0}/> + </Grid> -class CollectionPage extends Component { - render() { - return ( - <h1> Página visulizar coleção</h1> - ); - } - } + <Grid container item xs={12} direction="row" justify="center" alignItems="center" style={{backgroundColor: '#f4f4f4'}}> + <Grid item xs={10}> + <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), + id: i.collectionable.id, + } + }) + : [] + }/> + + </Grid> + <Grid container item xs={12} style={{marginTop: 40}} ref={comment_ref}> + <CollectionCommentSection id={collection_id}/> + </Grid> + </Grid> + </Grid> + ); +} -export default CollectionPage; +const mainContainerStyle = { + marginTop: '5%', +}