diff --git a/components/admin-drawer/component.js b/components/admin-drawer/component.js new file mode 100644 index 00000000..11915e9e --- /dev/null +++ b/components/admin-drawer/component.js @@ -0,0 +1,76 @@ +import React from 'react' +import styled from 'styled-components' + + +const StyledAdminDrawer = styled.div` + margin-left:auto; + margin-right:auto; + margin-top:2.5rem; + margin-bottom:2.5rem; + padding-right: 15px; + border-right: 1px solid #dae1e7; + @media (max-width: 700px) { + width:100%; + border-right: none; + margin-bottom:1.5rem; + + } +` + +const DrawerList = styled.div` + display: flex; + flex-direction: column; + +` + +const DrawerItem = styled.div` +cursor:pointer; +background: white; +border: none; +font-size: 1.6rem; +text-align: left; +padding: 15px; +color: #454246 +&:not(:last-child) { +border-bottom: solid 1px #dae1e7; +} +` + +const buttons = [ + { + "key":1, + 'name': 'Etiquetas', + 'value': 'tags' + }, + { + "key":2, + 'name':'Proyectos', + 'value':'projects' + }, + { + "key":3, + 'name':'Usuarios', + 'value':'users' + } + +] + +const AdminDrawer = (props) => ( + + + {buttons.map((button,idx) => props.changeSection(button.value)}> + + {button.name} + )} + + + + +) + +AdminDrawer.propTypes = { +} + +export default AdminDrawer diff --git a/components/admin-edit-user/component.js b/components/admin-edit-user/component.js new file mode 100644 index 00000000..7854b7e2 --- /dev/null +++ b/components/admin-edit-user/component.js @@ -0,0 +1,315 @@ +import React, { Component } from 'react' +import styled from 'styled-components' +import FileBase64 from 'react-file-base64' +import Jimp from 'jimp' +import jump from 'jump.js' +import ProfileForm from '../../elements/profile-form/component' +import ProfileAvatar from '../../elements/profile-avatar/component' +import ProfileName from '../../elements/profile-name/component' +import ProfileLabel from '../../elements/profile-label/component' +import ProfileInput from '../../elements/profile-input/component' +import ProfileSelect from '../../elements/profile-select/component' +import ProfileButtonWrapper from '../../elements/profile-button-wrapper/component' +import ProfileTags from '../../elements/profile-tags/component' +import Alert from '../../elements/alert/component' +import SubmitInput from '../../elements/submit-input/component' +import WithDocumentTagsContext from '../../components/document-tags-context/component' +import { withRouter } from 'next/router' + +import getConfig from 'next/config' + +const { publicRuntimeConfig: { + API_URL, +} } = getConfig() + +const TagsNotificationCheckboxDiv = styled.div` + width: 350px; + display: flex; + line-height: 15px; + margin-top: 3px; + font-size:13px; + & > input { + margin-right: 7px; + margin-bottom: auto; + } +` + +const ButtonLink = styled.button` + background-color: #5c97bc; + font-size: 1.2rem; + border-style: none; + color: var(--white); + font-family: var(--bold); + padding: 0.7em 1.8em; + background-color: #5c97bc; + font-size: 1.4rem; + margin: 1em 0 0; +` + +const InputErrorSpan = styled.span` + color: red; + font-size: 1.2rem; +` + +const genderOptions = [ + { 'name': '', 'value': '' }, + { 'name': 'Masculino', 'value': 'Masculino' }, + { 'name': 'Femenino', 'value': 'Femenino' }, + { 'name': 'Otro', 'value': 'Otro' }, + { 'name': 'Prefiero no especificar', 'value': 'Prefiero no especificar' } +] + +class UserEdit extends Component { + state = { + avatar: null, + user: null, + occupation: "", + gender: "", + party: "", + birthday: "", + province: "", + editMode: true, + files: [], + allTags: [], + tags: [], + tagsMaxReached: false, + tagsNotification: "", + isLoading: true, + status: "pending", + }; + + async componentWillMount() { + this.setState({ allTags: await this.props.fetchDocumentTags() }); + } + + componentDidMount() { + this.fetchUser(); + } + + fetchUser() { + fetch(`${API_URL}/api/v1/users/${this.props.router.query.user}`) + .then((resp) => resp.json()) + .then((user) => + this.setState({ + user: user, + occupation: user.fields.occupation || "", + gender: user.fields.gender || "", + party: user.fields.party || "", + birthday: user.fields.birthday || "", + province: user.fields.province || "", + tags: user.fields.tags || [], + tagsNotification: user.fields.tagsNotification || "", + isLoading: true, + }) + ) + .catch((err) => console.error(err)); + } + + // Callback~ + getFiles = async (files) => { + Jimp.read(Buffer.from(files.base64.split("base64,")[1], "base64")) + .then(async (image) => { + let optimizedImage = await image + .cover(150, 150) + .quality(90) + .getBase64Async(Jimp.MIME_JPEG); + this.setState({ avatar: optimizedImage }); + }) + .catch((err) => { + console.error(err); + }); + this.setState({ files: files }); + }; + + handleChange = (e) => { + const target = e.target; + const value = target.value; + const name = target.name; + this.setState({ + [name]: value, + }); + }; + + handleTagClick = (tag) => { + if (this.state.tagsMaxReached) this.setState({ tagsMaxReached: false }); + + const clickedTagId = tag._id; + if (this.state.tags.includes(clickedTagId)) + this.setState((prevState) => ({ + tags: prevState.tags.filter((tagId) => tagId != clickedTagId), + })); + else { + if (this.state.tags.length == 6) this.setState({ tagsMaxReached: true }); + else + this.setState((prevState) => ({ + tags: prevState.tags.concat(clickedTagId), + })); + } + }; + + handleSubmit = async (e) => { + e.preventDefault(); + const newData = { + fields: { + occupation: this.state.occupation || "", + gender: this.state.gender || "", + birthday: this.state.birthday || "", + province: this.state.province || "", + party: this.state.party || "", + tags: this.state.tags || "", + tagsNotification: this.state.tagsNotification || "", + }, + }; + if (this.state.avatar) { + newData.avatar = this.state.avatar; + } + await ( + await fetch(`${API_URL}/api/v1/users/${this.state.user._id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + Authorization: "Bearer " + this.props.token, + }, + body: JSON.stringify(newData), + }).then((res) => { + this.setState({ status: res.status === 200 ? "success" : "error" }); + return res; + }) + ).json(); + jump(-1000); + }; + + toggleTagsCheckboxChange = () => { + this.setState(({ tagsNotification }) => ({ + tagsNotification: !tagsNotification, + })); + }; + + dismissAlert = () => { + this.setState({ + status: "pending", + }); + }; + + render() { + const { user, isLoading, status } = this.state; + if (user) { + return ( + <> + + + {`${user.surnames}, ${user.names}`} + {/* */} + + {isLoading ?

...

: null} + {this.state.editMode ? ( +
+ + Imagen de perfil + + + + Fecha de Nacimiento + + + + Género + + + + Provincia / Localidad + + + + Ocupación + + + + Bloque + + + + + Etiquetas de interés + + + Deseo recibir notificaciones de futuros proyectos asociados + a mis etiquetas de interés + + {this.state.tagsMaxReached && ( + + Se pueden elegir hasta 6 etiquetas de interés + + )} + + + + + +
+ ) : null} +
+ {(status === "success" || status === "error") && ( + + {status === "success" + ? "Los cambios que ha realizado en el perfil fueron guardados con éxito." + : "Los cambios que ha tratado de guardar no han podido ser procesados. Le aconsejamos que lo intente nuevamente."} + + )} + + ); + } else { + return
; + } + } +} + +export default WithDocumentTagsContext(withRouter(UserEdit)) diff --git a/components/admin-projects/component.js b/components/admin-projects/component.js new file mode 100644 index 00000000..73cbea52 --- /dev/null +++ b/components/admin-projects/component.js @@ -0,0 +1,548 @@ +import React, { Component } from 'react' +import styled from 'styled-components' +import Router,{ withRouter } from 'next/router' +import TitleContent from '../title-content-admin/component' +import getConfig from 'next/config' +import WithUserContext from '../../components/with-user-context/component' +import ProjectTableItem from '../../components/project-table-item/component' +import Search from '../../elements/search/component' +import { clockO } from 'react-icons-kit/fa' +import { plus } from 'react-icons-kit/feather' + + +const { publicRuntimeConfig: { API_URL } } = getConfig() + +const getClosingDate = () => { + let closingDate = new Date() + closingDate.setDate(closingDate.getDate() + 30) + closingDate.setHours(0, 0, 0, 0) + return closingDate.toISOString() +} + +const newDocument = { + 'published': false, + 'closed': false, + 'customForm': 'project-form', + 'content': { + 'title': 'Mi nuevo proyecto', + 'imageCover': null, + 'youtubeId': null, + 'customVideoId': null, + 'sendTagsNotification': true, + 'fundation': { + 'object': 'value', + 'document': { + 'object': 'document', + 'data': { + }, + 'nodes': [ + { + 'object': 'block', + 'type': 'paragraph', + 'data': { + }, + 'nodes': [ + { + 'object': 'text', + 'leaves': [ + { + 'object': 'leaf', + 'text': 'Esta sección es un espacio para redactar un texto que sirve para presentar el proyecto, explicar el contexto (de donde surge, su importancia, etc.), e invitar la ciudadanía a participar. Es muy importante mencionar qué tipo de aportes ciudadanos se esperan. El proyecto tiene que estar explicado de manera muy simple, la redacción debe ser fácil de entender.', + 'marks': [ + ] + } + ] + } + ] + } + ] + } + }, + 'articles': { + 'object': 'value', + 'document': { + 'object': 'document', + 'data': { + }, + 'nodes': [ + { + 'object': 'block', + 'type': 'title', + 'data': { + }, + 'nodes': [ + { + 'object': 'text', + 'leaves': [ + { + 'object': 'leaf', + 'text': 'Art. 1º.', + 'marks': [ + ] + } + ] + } + ] + }, + { + 'object': 'block', + 'type': 'paragraph', + 'data': { + }, + 'nodes': [ + { + 'object': 'text', + 'leaves': [ + { + 'object': 'leaf', + 'text': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam sed purus justo. Nam tempus ligula nec est scelerisque aliquet. Phasellus pretium rhoncus pharetra. Duis dapibus felis neque.', + 'marks': [ + ] + } + ] + } + ] + }, + { + 'object': 'block', + 'type': 'title', + 'data': { + }, + 'nodes': [ + { + 'object': 'text', + 'leaves': [ + { + 'object': 'leaf', + 'text': 'Art. 2°.', + 'marks': [ + ] + } + ] + } + ] + }, + { + 'object': 'block', + 'type': 'paragraph', + 'data': { + }, + 'nodes': [ + { + 'object': 'text', + 'leaves': [ + { + 'object': 'leaf', + 'text': 'Fusce elementum posuere dolor id mattis. Sed magna arcu, rutrum eu pellentesque nec, feugiat sit amet lorem. Fusce volutpat, dolor a pretium fermentum, felis justo rhoncus nisl, vel mollis est diam mollis nisl. Sed aliquet erat sed ipsum lacinia, feugiat interdum ante pulvinar. Integer ut consectetur velit.', + 'marks': [ + ] + } + ] + } + ] + }, + { + 'object': 'block', + 'type': 'title', + 'data': { + }, + 'nodes': [ + { + 'object': 'text', + 'leaves': [ + { + 'object': 'leaf', + 'text': 'Art. 3°.', + 'marks': [ + ] + } + ] + } + ] + }, + { + 'object': 'block', + 'type': 'paragraph', + 'data': { + }, + 'nodes': [ + { + 'object': 'text', + 'leaves': [ + { + 'object': 'leaf', + 'text': 'In id neque posuere, dictum arcu vitae, euismod nulla. Integer eu euismod ipsum. In aliquet nisl mi, nec vulputate urna hendrerit eu. Integer in mi at quam luctus posuere. Integer magna sem, viverra non ultrices vitae, varius in mi.', + 'marks': [ + ] + } + ] + } + ] + } + ] + } + }, + 'closure': null, + 'closingDate': getClosingDate() + } +} + +const StyledProjectsAdmin = styled.div` + +` +const masonryOptions = { + transitionDuration: 0 +} +const Section =styled.div` +margin-left: '0em'; + +display: flex; +flex-wrap: wrap; +flex-direction: column; +align-items: flex-start; +box-sizing: border-box; +background-color: #fff; +> h2 { + color: #2c4c61; + } +} +@media (max-width:700px){ + padding: 35px 10px; +} +` + +const LoadMoreButtonContainer = styled.div` +width: 100%; +display: flex; +justify-content: center; +` + +const LoadMoreButton = styled.div` +margin: 0 auto; +font-size: 2.2rem; +padding: 5px 25px; +border-radius: 4px; +border: 1px solid #2c4c61 +cursor: pointer +color: #2c4c61; +&:hover{ + background-color: #2c4c61; + color: #FFF +} +&:first-child{ + margin-left: 0; +} +&:last-child{ + margin-right: 0; +} +&.disabled{ + color: #777; + border-color: #777; +} +` +const MessagePaginator = styled.div` +font-size: 2.5rem; +color: #454246; +font-family: var(--bold); +text-align: center; +width: 100%; +` + + +const Icon = styled.div` + width: 18px; + height: 15px; + background-image: url(${(props) => `/static/assets/${props.icon}`}); + background-size: cover; + background-repeat: no-repeat; + display: inline-block; + position: relative; + top: 2px; + @media(max-width:700px){ +filter:grayscale(100%) brightness(54%) sepia(100%) hue-rotate(-180deg) saturate(700%) contrast(0.8); +} +` + +const ProjectsTable = styled.table` + width: 100%; + margin: 20px 0; +` +const ProjectsTableHead = styled.thead` + +` +const ProjectsTableBody = styled.tbody` + +` +const ProjectsTableRow = styled.tr` + +` +const ProjectsTableCell = styled.td` + padding: 5px 2px; + font-size: 13px; + text-align: ${(props) => props.centered ? 'center' : 'left'}; + border-bottom: 1px solid #cacaca; + & > a{ + color: #5c97bc + } + & > a:hover{ + color: red; + } +` +const ProjectsTableHeader = styled.th` + font-family: var(--medium); + font-size: 16px; + color: #2c4c61; + text-align: ${(props) => props.centered ? 'center' : 'left'}; + width: ${(props) => `${props.width}px` || 'auto'}; + border-bottom: 1px solid #CACACA; + padding: 2px 5px; + ${(props) => props.hiddenMobile && '@media(max-width:700px){display: none;}'} +` + +const ButtonTable = styled.div` + padding: 5px 20px; + margin: 10px; + border: 1px solid #5c97bc; + border-radius: 5px; + color: #5c97bc; + font-size: 17px; + text-align: center; + float: ${(props) => props.float || 'none'}; + @media (max-width: 700px) { + float: none; + } + &:hover { + background-color: #5c97bc; + color: #FFF; + cursor: pointer; + } + &[disabled]{ + cursor: not-allowed; + color: #999; + border-color: #999; + } + &[disabled]:hover, + &[disabled]:active, + &[disabled]:focus { + background-color: #eee; + color: #999; + border-color: #999; + font-weight: normal; + font-family: unset; + } +` + +const ButtonTableDisabled = styled.div` + padding: 5px 20px; + margin: 10px; + // width: 80%; + border: 1px solid #868686; + border-radius: 5px; + color: #868686; + font-size: 17px; + float: ${(props) => props.float || 'none'}; + @media (max-width: 700px) { + float: none; + } +` + + +class ProjectsAdmin extends Component{ + state={ + projects: [], + page: 1, + noMore: false, + showAlert: false, + alertText: null, + alertType: null, + isLoading: false, + fetching: true, + fetchMoreAvailable: false, + query: { + created: 'ASC', + limit: 10, + page: 1, + closed: null, + author: null + } + } + + getDocuments() { + + this.setState({ + fetching: true + }, () => this.fetchProjects(this.props.token)) + + } + + createQuery = (sort) => { + let theQuery = '?' + + Object.keys(sort).map(function (key) { + return encodeURIComponent(key) + '=' + + encodeURIComponent(sort[key]) + }).join('&'); + + return theQuery + } + + fetchProjects = (token, userId) => { + + let query = this.state.query + if(this.props.router.query.user) query.author = this.props.router.query.user + let currentQuery = this.createQuery(query); + fetch(`${API_URL}/api/v1/documents?${currentQuery}`, { + 'headers': { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + token + } + }) + .then(resp=>resp.json()) + .then(projects=> + this.setState((prevState) => { + return { + projects: prevState.projects.concat(projects.results), + fetchMoreAvailable: projects.pagination.page < projects.pagination.pages, + fetching: false + } + }) + ) + .catch(err=>console.error(err)) + + } + + componentDidMount(){ + this.getDocuments() + } + + + + getMoreDocuments(){ + this.setState({ + query:{ + ...this.state.query, + page: this.state.query.page + 1 + } + },()=>this.getDocuments() + ) +} + +toggleSort = (parameter, value) => { + let newQuery = this.state.query + newQuery[parameter] = value + newQuery.page = 1 + this.setState({ + projects: [], + query: newQuery + }, () => { + this.getDocuments() + }) + +} + + createProject = async () => { + this.setState({ + isLoading: true + }) + fetch(`${API_URL}/api/v1/documents`, { + 'method': 'POST', + 'headers': { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + this.props.authContext.keycloak.token + }, + 'body': JSON.stringify(newDocument) + }) + .then((res) => { + if (!res.ok) { + throw new Error('Forbidden') + } + return res.json() + }) + .then((content) => { + Router.push({ + pathname: '/propuesta', + query: { + id: content._id + } + }) + }) + .catch((err) => { + this.setState({ + menu: false, + showAlert: true, + isLoading: false, + alertStatus: 'error', + alertText: 'Ocurrió un error al crear una nueva propuesta (¿Limite alcanzado?)' + }) + console.error(err) + }) + } + + render(){ + const { + projects, + fetchMoreAvailable, + isLoading, + fetching + } = this.state + return( + + + proyectos + { + isLoading + ?   Creando nuevo proyecto... Espere unos segundos... + :   Agregar una nueva propuesta + } + + + this.toggleSort('textFilter', e.target.value)} /> +
+ + + + Nombre + Status + {window.location.pathname === '/admin' && Autor} + Aportes + Apoyos + Fecha creación + Fecha de cierre + Acciones + + + + + {projects && projects.map((p, i) => )} + + + { + this.state.showAlert && + + {this.state.alertText} + + } + { + !fetching && fetchMoreAvailable && + this.getMoreDocuments()}>Cargar más + + } + { + fetching && Cargando... + } + { + !fetching && !fetchMoreAvailable && + No hay más propuestas de leyes + } + +
+ +
+)} +} + +ProjectsAdmin.propTypes = { +} + +// export default withRouter(ProjectsAdmin) + +export default WithUserContext(withRouter(ProjectsAdmin)) \ No newline at end of file diff --git a/components/admin-tags/component.js b/components/admin-tags/component.js new file mode 100644 index 00000000..fbcb7c55 --- /dev/null +++ b/components/admin-tags/component.js @@ -0,0 +1,147 @@ +import React, { Component } from 'react' +import styled from 'styled-components' + +import WithDocumentTagsContext from '../../components/document-tags-context/component' + +import getConfig from 'next/config' +import TagsList from '../../elements/tag-list/component' +import TagNew from '../../elements/tag-form/component' +import Modal from '../modal/component' +import TitleContent from '../title-content-admin/component' + +const { publicRuntimeConfig: { API_URL } } = getConfig() + + + + +const StyledTagsAdmin = styled.div` + +` + +const TagsContent = styled.div` +display: flex; +flex-wrap: wrap; + +` + +const Tag = styled.div` +margin-bottom: 5px; +margin-right: 5px; +background:#B6D5F2; +color: #4C4C4E; +border-radius:5px; +font-weight: 600; +font-family: var(--italic); +padding:8px; +font-size:12px +line-height: 15px; +text-align: center; +letter-spacing: 1.1px; +text-transform: capitalize; +` + +const ModalButton = styled.button` +margin:23px 8px; +min-width: 125px; +max-width: 230px; +height: 39px; +background-color: ${(props) => props.type === 'deleteButton' ? '#CF1419': '#5c97bc'}; +font-size: 1.4rem; +color: var(--white); +border-style: none; +cursor: pointer; +padding: 0 2rem; +font-family: var(--bold); +` + +class TagsAdmin extends Component{ + state = { + allTags:null, + modalActive:false, + tagToDelete:null} + + componentDidMount(){ + + this.fetchtags() + } + + fetchtags = () =>{ + this.props.fetchDocumentTags() + .then(documentTags => { + const parsedTags = documentTags.map(documentTag => ({ id: documentTag._id, text: documentTag.name })) + + this.setState({ + allTags: parsedTags + }) + }) + .catch(err=>console.error(err)) + } + + deleteTag = (tag)=>{ + this.setState({ + tagToDelete: tag, + modalActive: true + }) + } + + + confirmDeleteTag = async () => { + await fetch(`${API_URL}/api/v1/document-tags/${this.state.tagToDelete.id}`,{ + method:'DELETE', + headers: { + 'Authorization': 'Bearer ' + this.props.token + }, + }) + this.setState({tagToDelete:null,modalActive: false}) + this.fetchtags() + } + + addTag = async (newTag) =>{ + try{ + if(newTag.length > 0){ + + + await fetch(`${API_URL}/api/v1/document-tags`,{ + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + this.props.token + }, + body:JSON.stringify( + {'name':newTag, + 'key':newTag.replace(/ /g,"-")} + ) + }) + this.fetchtags() + }else{ + throw Error() + } + }catch(error){ + console.log(error); + } + } + + render(){ + const {allTags, modalActive, tagToDelete} = this.state + return( + + tags + + {allTags && } + {modalActive && this.setState({modalActive:false})} + title={`¿seguro desea eliminar ${tagToDelete.text}?`} + footer={
+ this.setState({modalActive:false})} type='cancel'>Cancelar + this.confirmDeleteTag()} type='deleteButton'>Eliminar +
}/>} +
+ ) + } + } + +TagsAdmin.propTypes = { +} + +export default WithDocumentTagsContext(TagsAdmin) diff --git a/components/admin-users/component.js b/components/admin-users/component.js new file mode 100644 index 00000000..3fc845ea --- /dev/null +++ b/components/admin-users/component.js @@ -0,0 +1,169 @@ +import React, { Component } from 'react' +import styled from 'styled-components' +import TitleContent from '../title-content-admin/component' +import getConfig from 'next/config' +import CardUser from '../card-users/component' +import Search from '../../elements/search/component' +const { publicRuntimeConfig: { API_URL } } = getConfig() + +const StyledUsersAdmin = styled.div` +width:100% + +` + +const Content = styled.div` +display:flex; +flex-wrap: wrap; +margin: 20px 0 +` + +const LoadMoreButtonContainer = styled.div` + width: 100%; + display: flex; + justify-content: center; +` +const MessagePaginator = styled.div` + font-size: 2.5rem; + color: #454246; + font-family: var(--bold); + text-align: center; + width: 100%; +` + +const LoadMoreButtonNoUser = styled.div` +margin: 0 auto; +font-size: 2.2 +rem; +padding: 5px 25px; +border-radius: 4px; +border: 1px solid #2c4c61 +cursor: pointer +color: #2c4c61; +&:hover{ + background-color: #2c4c61; + color: #FFF +} +&:first-child{ + margin-left: 0; +} +&:last-child{ + margin-right: 0; +} +&.disabled{ + color: #777; + border-color: #777; +} +` + + + +class UsersAdmin extends Component{ + state = { + usersList:[], + fetching:true, + fetchMoreAvailable:true, + query:{ + page:1, + limit:10 + } + } + + createQuery = (sort) => { + let theQuery = '?' + + Object.keys(sort).map(function (key) { + return encodeURIComponent(key) + '=' + + encodeURIComponent(sort[key]) + }).join('&'); + return theQuery + } + + componentDidMount(){ + this.fetchUsers() + } + + getMoreUsers = ()=> { + try{ + this.setState({ + query:{ + ...this.state.query, + page: this.state.query.page + 1 + } + },()=>this.fetchUsers() + ) + } + catch(err){ + console.log(err); + } + } + + fetchUsers = () => { + const {query} = this.state + const currentQuery = this.createQuery(query) + fetch(`${API_URL}/api/v1/users${currentQuery}`,{ + headers: { + Authorization: `Bearer ${this.props.token}`, + 'Content-Type': 'application/json' + }, + method: 'GET' + }) + .then(resp=>resp.json()) + .then(data=>{ + + this.setState((prevState) =>{ + return{ + usersList: prevState.usersList.concat(data.results), + fetching:false, + fetchMoreAvailable: data.pagination.page < data.pagination.pages + } + }) + }) + .catch(err=>console.error(err)) + + + } + + toggleSort = (parameter, value) => { + let newQuery = this.state.query + newQuery[parameter] = value + newQuery.page = 1 + this.setState({ + usersList: [], + query: newQuery + }, () => { + this.fetchUsers() + }) + + } + + render(){ + const { usersList, fetching, fetchMoreAvailable } = this.state + return( + + users + this.toggleSort('search', e.target.value)} /> + + + {usersList && usersList.map((user, idx) => + + )} + { + !fetching && fetchMoreAvailable && + this.getMoreUsers()}>Ver más + + } + { + fetching && Cargando... + } + { + !fetching && !fetchMoreAvailable && + No hay más propuestas de leyes + } + + + +)}} + +UsersAdmin.propTypes = { +} + +export default UsersAdmin diff --git a/components/card-users/component.js b/components/card-users/component.js new file mode 100644 index 00000000..de6b0228 --- /dev/null +++ b/components/card-users/component.js @@ -0,0 +1,53 @@ +import React from 'react' +import Link from 'next/link' +import PropTypes from 'prop-types' +import styled from 'styled-components' +import CardUserHeader from '../../elements/card-user-header/component' +import CardUserActions from '../../elements/card-user-actions/component' +import router from 'next/router' + +const CardContainer = styled.div` +margin: 0 1% 30px; +width: 23%; +box-shadow: 0 4px 20px 0 rgba(0,0,0,0.05); +background-color: #FFF; +border: solid 1px #e9e9e9; +box-sizing: border-box; +display: flex; +display: flex; +flex-direction: column; +justify-content: space-between; +position: relative; +@media (max-width: 1408px) { + width: 31%; + } +@media (max-width: 1216px) { + width: 48%; + } +@media (max-width: 600px) { + width: 100%; + } +` + +const CardUser = ({ user }) => { + const edit = ()=>{ + router.push(`/admin?section=userEdit&user=${user._id}`); + } + const projects = ()=>{ + router.push(`/admin?section=projects&user=${user._id}`) + } + return ( + + + + + + ) +} + +CardUser.propTypes = { + user: PropTypes.object.isRequired, + +} + +export default CardUser diff --git a/components/card/component.js b/components/card/component.js index a48f917c..131cf0da 100644 --- a/components/card/component.js +++ b/components/card/component.js @@ -3,17 +3,15 @@ import Link from 'next/link' import PropTypes from 'prop-types' import styled from 'styled-components' import CardHeader from '../../elements/card-header/component' -import CardContent from '../../elements/card-content/component' import CardSocial from '../../elements/card-social/component' -import WithDocumentTagsContext from '../../components/document-tags-context/component' +import CardContent from '../../elements/card-content/component' const CardContainer = styled.div` margin: 0 1% 30px; width: 23%; box-shadow: 0 4px 20px 0 rgba(0,0,0,0.05); -background-color: #ffffff; +background-color: #F1ECEA; border: solid 1px #e9e9e9; -background: #fff; box-sizing: border-box; cursor: pointer; display: block; @@ -34,25 +32,27 @@ const Card = ({ project, tags }) => ( {/* */} - 0} img={`/static/assets/images/${tags && project.currentVersion.content.tags && project.currentVersion.content.tags.length > 0 ? tags.find(x => project.currentVersion.content.tags[0] == x.value).key : 'trama-default'}.jpg`} published={project.published} /> + {/* 0} img={`/static/assets/images/${tags && project.currentVersion.content.tags && project.currentVersion.content.tags.length > 0 ? tags.find(x => project.currentVersion.content.tags[0] == x.value).key : 'trama-default'}.jpg`} published={project.published} /> */} + 0} - party={project.author.fields && project.author.fields.party ? project.author.fields.party : ''} /> - + closingDate={project.currentVersion.content.closingDate} + creationDate={project.currentVersion.createdAt} + tags={project.currentVersion.content.tags} + tagList={tags} /> + ) Card.propTypes = { - project: PropTypes.object.isRequired + project: PropTypes.object.isRequired, + tags: PropTypes.array } -export default WithDocumentTagsContext(Card) +export default Card diff --git a/components/comments-grid/component.js b/components/comments-grid/component.js index 64b7f007..104655df 100644 --- a/components/comments-grid/component.js +++ b/components/comments-grid/component.js @@ -5,7 +5,7 @@ import Icon from 'react-icons-kit' import {timesCircle} from 'react-icons-kit/fa/timesCircle' const StyledCommentsGrid = styled.div` position: fixed; - top: 0; + top: 8rem; right: 0; display: flex; flex-direction: column; @@ -18,7 +18,10 @@ const StyledCommentsGrid = styled.div` min-width: 300px; width: 33%; ${(props) => !props.show && 'display: none;'} -` + @media (max-width:700px){ + top:10rem; + } + ` const CloseGrid = styled.div` padding: 7px; font-size: 12px; diff --git a/components/como-participar/component.js b/components/como-participar/component.js index f73d7655..0504f37b 100644 --- a/components/como-participar/component.js +++ b/components/como-participar/component.js @@ -24,7 +24,7 @@ export default () => ( ¿Cómo funciona? - Los Diputados suben propuestas y proyectos de ley para que puedan ser enriquecidos: se pueden hacer aportes, comentarios y sugerencias + Las y los diputados suben propuestas y proyectos de ley para que puedan ser enriquecidos con aportes, comentarios y sugerencias ¿Qué es una propuesta? @@ -33,18 +33,20 @@ export default () => ( Una propuesta de ley es el estado anterior al proyecto de ley, es decir, antes de iniciado el trámite parlamentario. - Los diputados analizarán los aportes realizados por la ciudadanía en la propuesta y a partir de esto realizarán los cambios que consideren necesarios para la - versión final del texto. + Las y los diputados analizarán los aportes realizados por la ciudadanía en la propuesta y a partir de esto realizarán los cambios que consideren necesarios para la versión final del texto. ¿Qué entendemos por aporte? - A través de esta plataforma, la ciudadanía puede hacer comentarios generales, para dar su opinión o postura general sobre la propuesta de ley. Además, puede realizar aportes puntuales seleccionando una parte específica del texto y haciendo un aporte particular. + + A través de esta plataforma, la ciudadanía puede hacer comentarios generales, para dar su opinión o postura general sobre la propuesta de ley. Además, puede realizar aportes puntuales seleccionando una parte específica del texto y haciendo un aporte particular. + + + Las y los diputados analizarán los aportes. En la medida en se realicen cambios a la propuesta original se generarán nuevas versiones de la propuesta. Así, las y los usuarios cuyos aportes fueran incorporados, serán colaboradores en la redacción de la propuesta final. - Los diputados analizarán los aportes. En la medida en se realicen cambios a la propuesta original se generarán nuevas versiones de la propuesta. Así, las y los usuarios cuyos aportes fueran incorporados, serán colaboradores en la redacción de la propuesta. + Para más detalles sobre las funcionalidades básicas de esta plataforma descargue/consulte el Manual de usuario - Para más detalles sobre las funcionalidades básicas de esta plataforma descargue/consulte el Manual de usuario ) diff --git a/components/contacto/component.js b/components/contacto/component.js index 6b7a6a1a..9430d121 100644 --- a/components/contacto/component.js +++ b/components/contacto/component.js @@ -12,13 +12,13 @@ export default () => ( Contacto - Programa de Modernización Parlamentaria, Innovación, Transparencia y Fortalecimiento Democrático. + Dirección General de Innovación, Planificación y Nuevas Tecnologías. - leyesabiertas@hcdn.gob.ar. + innovacion@hcdn.gob.ar. - (+5411) 6075-7100 int. 5091 + (+54 11) 6075-0000 interno 5003. diff --git a/components/faq/component.js b/components/faq/component.js index 17666b61..a12c3a0f 100644 --- a/components/faq/component.js +++ b/components/faq/component.js @@ -27,7 +27,7 @@ export default () => ( ¿Los diputados y las diputadas leen los comentarios? - Si, los diputados y las diputadas administran y moderan sus propuestas y proyectos de ley. Leen y analizan los comentarios para realizar cambios que consideran necesarios para mejorar la propuesta de ley. + Si, los diputados y las diputadas administran y moderan sus propuestas y proyectos de ley; leen y analizan los comentarios para realizar cambios que consideran necesarios para mejorar la propuesta de ley. diff --git a/components/footer-bar/component.js b/components/footer-bar/component.js index e1e4e28a..91bf7a7b 100644 --- a/components/footer-bar/component.js +++ b/components/footer-bar/component.js @@ -22,6 +22,9 @@ const FooterBar = styled.div` max-width:220px; box-sizing:border-box; + @media (max-width: 760px) { + padding:10px 12px + } } ` diff --git a/components/link-bar/component.js b/components/link-bar/component.js index 66af73e5..0f3d7d49 100644 --- a/components/link-bar/component.js +++ b/components/link-bar/component.js @@ -8,18 +8,52 @@ const Wrapper = styled.div` align-items:center; list-style:none; padding-left:0; - > a { - color: #203340; - display: inline-block; - padding: 10px 20px; - font-size: 1.6rem; - &:last-child{ - padding-right:0px; + white-space: nowrap; + @media(max-width:928px){ + flex-direction: column; + + } + @media(max-width:760px){ + flex-direction: column; + font-size: 2.6rem; + align-items: flex-start; + width:100% + } + > div { + @media(max-width:760px){ + border-bottom: 1px solid #D6D6D6; + width:100%; + display: flex; + justify-content: space-between + align-items:center + flex-wrap: wrap; + } - @media (max-width:760px){ - :first-child{ - height:40px; + > a { + color: #203340; + display: inline-block; + padding: 10px 20px; + font-size: 1.6rem; + flex-grow: 1 + @media(max-width:928px){ + flex-direction: column; + padding-left: 0; + } + @media(max-width:760px){ + font-size: 2.6rem; + padding-left: 48px; + padding-bottom: 12px + + } + &:last-child{ + padding-right:0px; + } + + } + > i { + padding-right:16px + cursor:pointer } } ` diff --git a/components/logged-user-bar/component.js b/components/logged-user-bar/component.js index 048490c8..486b27b4 100644 --- a/components/logged-user-bar/component.js +++ b/components/logged-user-bar/component.js @@ -6,9 +6,14 @@ import styled from 'styled-components' const StyledBar = styled.div` display:flex; justify-content:flex-end; -width:33%; margin-top:auto; +margin-bottom:auto; padding-bottom:.5rem; +@media(max-width:760px){ + justify-content: center; + border-bottom: 1px solid #D6D6D6 + + } ` const LoggedUserBar = ({ children }) => ( diff --git a/components/logged-user/component.js b/components/logged-user/component.js index b5a2b18b..396933a7 100644 --- a/components/logged-user/component.js +++ b/components/logged-user/component.js @@ -11,7 +11,35 @@ const StyledLoggedUser = styled.div` background: #fff; box-sizing: border-box; cursor: pointer; + @media(max-width:760px){ + width:100% + justify-content:space-between; + padding-bottom:10px + } ` +const UserName = styled.div` +height: 22px; +font-size: 1.6rem<; +color: #5c97bc; +border-style: none; +padding-left:1.5rem; +padding-bottom:2rem; +cursor: pointer; +background:#fff; +color: #5c97bc; +display:none +@media(max-width:760px){ + display:block; + height:auto; + padding:0 + font-size: 2.6rem; + font-family:var(--bold); + width:100% + padding: 12px 0 10px 48px + text-align: start + } +` + const badge = (props) => { if (props.authContext.isAuthor) { if (props.authContext.user.fields) { @@ -42,6 +70,7 @@ const subtituloUsuario = (props) => { const LoggedUser = (props) => ( + {props.authContext.user.fullname} ( +
+ {active && ( + + hideModal()}> + + + {title} + hideModal()}> + + {children} + {footer} + + + )} +
+ ); + + Modal.propType = { + + } +export default Modal; \ No newline at end of file diff --git a/components/modal/modal.styles.js b/components/modal/modal.styles.js new file mode 100644 index 00000000..985f11cc --- /dev/null +++ b/components/modal/modal.styles.js @@ -0,0 +1,83 @@ +import styled from "styled-components"; + +export const ModalBlock = styled.div` + align-items: center; + bottom: 0; + justify-content: center; + left: 0; + overflow: hidden; + padding: 0.4rem; + position: fixed; + right: 0; + top: 0; + display: flex; + opacity: 1; + z-index: 400; +`; + +export const ModalOverlay = styled.a` + background: rgba(247, 248, 249, 0.75); + bottom: 0; + cursor: default; + display: block; + left: 0; + position: absolute; + right: 0; + top: 0; +`; + +export const ModalClose = styled.a` + float: right !important; + text-decoration: none !important; + cursor: pointer; + font-size: 1rem; +`; + +export const ModalContainer = styled.div` + background: #ffffff; + border-radius: 0.1rem; + display: flex; + flex-direction: column; + max-height: 75vh; + max-width: 850px; + padding: 0 0.8rem; + width: 100%; + animation: slide-down 0.2s ease 1; + z-index: 1; + box-shadow: 0 0.2rem 0.5rem rgba(48, 55, 66, 0.3); +`; + +export const ModalBody = styled.div` + overflow-y: auto; + padding: 30px 10px; + position: relative; +`; + +export const ModalHeader = styled.div` + display: flex; + flex-direction: row; + justify-content: space-between; + color: #303742; + padding: 20px 5px 10px 5px; +`; + +export const ModalTitle = styled.span` + font-size: 30px; + font-weight: 500; +`; + +export const ModalFooter = styled.div` + padding: 10px 0px; + text-align: right; +`; + +export const Button = styled.button` + background: #7b2cbf; + color: white; + font-size: 1em; + margin: 10px; + padding: 5px 10px; + border: 2px solid #7b2cbf; + border-radius: 3px; + cursor: pointer; +`; \ No newline at end of file diff --git a/components/mode-bar/component.js b/components/mode-bar/component.js index 683dfb72..3ab6ac2c 100644 --- a/components/mode-bar/component.js +++ b/components/mode-bar/component.js @@ -9,6 +9,8 @@ const StyledModeBar = styled.nav` width: 100%; // height: 45px; background: #FFF; + display: flex; + justify-content: space-between; // padding: 0 3%; // border-bottom: solid 1px #dae1e7; @media(max-width:700px){ diff --git a/components/participate-item/component.js b/components/participate-item/component.js index fe39a6c9..a0217e63 100644 --- a/components/participate-item/component.js +++ b/components/participate-item/component.js @@ -15,13 +15,35 @@ const ParticipateItem = styled.div` margin-left: auto; margin-right: auto; } + @media (max-width:700px){ + padding-left:auto!important; + padding-right:auto!important; + border-left:none!important; + border-right: none!important; + margin-left:0!important; + margin-right: 0!important; + } &:nth-child(even) { - padding-left:1rem; - padding-right:1rem; - border-left:1px solid #dae1e7; - border-right:1px solid #dae1e7; - margin-left:.5rem; - margin-right:.5rem; + position: relative; + &:after { + content: " "; + background-color: #dae1e7; + position: absolute; + top:25% + left: 0; + height: 50%; + width: 2px; + }; + &:before { + content: " "; + background-color: #dae1e7; + position: absolute; + top:25% + right: 0; + height: 50%; + width: 2px; + }; + } ` diff --git a/components/politicas-de-privacidad/component.js b/components/politicas-de-privacidad/component.js index 74ff0697..9c12705b 100644 --- a/components/politicas-de-privacidad/component.js +++ b/components/politicas-de-privacidad/component.js @@ -12,11 +12,11 @@ import StaticInfoButton from '../../elements/static-info-button/component' const StyledStaticInfo = styled.div` display: flex; justify-content: center; - background-image: url('/static/assets/header-background.jpg'); background-repeat: no-repeat; ` const buttons = [ { + key:'1', name: 'Políticas de privacidad', value: '#politicas' } @@ -28,7 +28,7 @@ const scroll = (target) => (e) => { export default () => ( - +
Políticas de privacidad @@ -105,7 +105,7 @@ export default () => ( {buttons.map((button, i) => ( + key={button.key}> {button.name} ))} diff --git a/components/project-body/component.js b/components/project-body/component.js index 16035f07..67d71d10 100644 --- a/components/project-body/component.js +++ b/components/project-body/component.js @@ -53,7 +53,7 @@ const BoldP = styled.p` ` -const ProjectBody = ({ project }) => ( +const ProjectBody = ({ project, isAdmin }) => ( { @@ -62,6 +62,7 @@ const ProjectBody = ({ project }) => ( {isAuthor && editMode && ( tags={project.currentVersion.content.tags} sendTagsNotification={project.currentVersion.content.sendTagsNotification} publishedMailSent={project.publishedMailSent} - setNewFields={setNewFields} /> + setNewFields={setNewFields} + isAdmin={isAdmin} + /> } { project.currentVersion.content.youtubeId && diff --git a/components/project-fields/component.js b/components/project-fields/component.js index b1787f7c..5c7d0eef 100644 --- a/components/project-fields/component.js +++ b/components/project-fields/component.js @@ -5,8 +5,13 @@ import es from 'date-fns/locale/es'; import ProfileLabel from '../../elements/profile-label/component' import EditorTitle from '../../elements/editor-title/component' import WithDocumentTagsContext from '../../components/document-tags-context/component' +import WithUserContext from '../with-user-context/component'; import ProfileTags from '../../elements/profile-tags/component' + +import getConfig from 'next/config' +const { publicRuntimeConfig: { API_URL } } = getConfig() + injectGlobal` //-------------------------------------- @@ -786,6 +791,17 @@ const InputField = styled.input` background-color: #f7f7f7 } ` +const SelectField = styled.select` + width: 100%; + height: 40px; + border: solid 1px #dae1e7; + background-color: #ffffff; + font-size: 1.4rem; + line-height: 1.5rem; + color: #203340; +` + + const TextareaField = styled.textarea` width: 100%; min-height: 250px; @@ -828,26 +844,74 @@ const TagsNotificationCheckboxDiv = styled.div` top: 2px; } ` +const VideoSource = styled.div` +margin-top:10px; +display:inline-block; +` + +const OptionSourceVideo = styled.input` +display:none; +` +const LabelOptionSource = styled.label` + font-size:1.4rem; + padding:14px; + border:solid 1px #dae1e7; + border-right:none; + cursor:pointer; + text-transform:capitalize; + display:inline-block; + background-color:${props => props.active && '#567B9A'}; + color:${props => props.active ? '#fff' : '#203340'}; + &:last-child{ + border-right:solid 1px #dae1e7; + } +` class ProjectFields extends Component { state = { title: null, + author: null, + usersList: [], + fetchingUsers: false, closingDate: null, imageCover: null, youtubeId: null, + prevYoutubeId:null, customVideoId: null, + prevCustomVideoId: null, youtubeURL: null, closure: null, tags: [], allTags: [], tagsMaxReached: false, sendTagsNotification: null, - publishedMailSent: null + publishedMailSent: null, + videoSource:null } + + fetchUsers = () => { + this.setState({fetchingUsers: true}) + fetch(`${API_URL}/api/v1/users`,{ + headers: { + Authorization: `Bearer ${this.props.authContext.keycloak.token}`, + 'Content-Type': 'application/json' + }, + method: 'GET' + }) + .then(resp=>resp.json()) + .then(users =>this.setState({ + usersList: users.results.sort((a, b) => (a.surnames - b.surnames)), + fetchingUsers:false, + })) + .catch(err=>console.error(err)) + + } + componentDidMount() { let { title, + author, closingDate, imageCover, youtubeId, @@ -858,17 +922,25 @@ class ProjectFields extends Component { publishedMailSent } = this.props + if (this.props.isAdmin) { + this.fetchUsers(); + } + this.setState({ title, + author, imageCover, youtubeId, youtubeURL: youtubeId ? 'https://www.youtube.com/watch?v=' + youtubeId : '', + prevYoutubeId:youtubeId ? 'https://www.youtube.com/watch?v=' + youtubeId : '', customVideoId: customVideoId || null, + prevCustomVideoId: customVideoId || null, closingDate: new Date(closingDate.split('T')[0].replace(/-/g, '\/')), closure: closure || null, tags: tags || [], sendTagsNotification, - publishedMailSent + publishedMailSent, + videoSource: youtubeId ? 'youtube' : customVideoId ? 'hcdn' : 'noVideo' }, () => { this.props.setNewFields(this.getBodyPayload()) @@ -888,6 +960,7 @@ class ProjectFields extends Component { getBodyPayload = () => { return { title: this.state.title, + author: this.state.author, imageCover: this.state.imageCover, closingDate: new Date(this.state.closingDate).toISOString(), youtubeId: this.state.youtubeId, @@ -913,18 +986,26 @@ class ProjectFields extends Component { } parseVideoId = () => { - let videoID = this.state.youtubeURL.split('v=')[1] || null - if (videoID) { - let ampersandPosition = videoID.indexOf('&') - if (ampersandPosition !== -1) { - videoID = videoID.substring(0, ampersandPosition) + let videoId = this.state.youtubeURL + if (videoId) { + if (videoId.indexOf('v=') > -1) { + videoId = videoId.split('v=')[1] + const ampersandPosition = videoId.indexOf('&') + if (ampersandPosition !== -1) { + videoId = videoId.substring(0, ampersandPosition) + } + } else if (videoId.indexOf('youtu.be/') > -1) { + videoId = videoId.split('youtu.be/')[1] + const ampersandPosition = videoId.indexOf('&') + if (ampersandPosition !== -1) { + videoId = videoId.substring(0, ampersandPosition) + } } + console.log(videoId); + this.setState({ + youtubeId: videoId + }, () => this.props.setNewFields(this.getBodyPayload())) } - this.setState({ - youtubeId: videoID - }, () => { - this.props.setNewFields(this.getBodyPayload()) - }) } handleInputChangeYoutube = (e) => { @@ -954,6 +1035,19 @@ class ProjectFields extends Component { } } + handleVideoSource = (e)=>{ + this.setState({videoSource : e.currentTarget.value}); + if (e.target.value === 'hcdn') this.setState({youtubeId: null, customVideoId: this.state.prevCustomVideoId || null},() => { + this.props.setNewFields(this.getBodyPayload()) + }) + if (e.target.value === 'youtube') this.setState({customVideoId: null, youtubeId: this.state.prevYoutubeId},() => { + this.props.setNewFields(this.getBodyPayload()) + }) + if (e.target.value === 'noVideo') this.setState({customVideoId: null, youtubeId: null},() => { + this.props.setNewFields(this.getBodyPayload()) + }) + } + toggleTagsNotificationCheckbox = () => { this.setState(({ sendTagsNotification }) => ( { @@ -982,6 +1076,15 @@ class ProjectFields extends Component { onChange={this.handleInputChange} placeholder='Hacer uso correcto de mayúsculas y minúsculas' /> + {this.props.isAdmin && + Autor/a: + + {this.state.usersList.map((u) => )} + + } {/* Ingrese la URL para la imagen de encabezado: + Video: + + + sin video + + Youtube + + camara de diputados + + + { this.state.videoSource === 'hcdn' && Ingrese el link del video (Reproductor oficial HCDN) (Opcional) Link invalido o vacio (El proyecto se publicará sin video) } NOTA: Ingrese solamente el dominio del video, sin "https://", que termina hasta ".mp4". (Ej: argos.hcdn.gob.ar/DMPARL/tutorial.mp4) - - {/* + } + { this.state.videoSource === 'youtube' && Ingrese el link del video de Youtube (Opcional) {!this.state.youtubeId && Link invalido o vacio (El proyecto se publicará sin video) } - */} + } + { this.state.videoSource === 'noVideo' && + + NOTA: El proyecto se publicará sin video + } Palabras de cierre props.img}'); + background-color: #395595; + //background-image: url('${(props) => props.img}'); background-size: cover; background-position: center; display: flex; @@ -40,6 +42,21 @@ const ProjectHeaderContainer = styled.div` align-items: flex-end; // display: block; ` +const InfoHeader = styled.div` +margin:30px +` +const SocialSection = styled.div` +display: flex; +` + +const ProgressBarWrapper = styled.div` +width:33% +@media(max-width:700px){ + width:50%; + margin-bottom: 16px; +} +` + const TopBarWrapper = styled.div` display: flex; flex-direction:row; @@ -73,78 +90,141 @@ const TopBarWrapper = styled.div` // } ` -const ProjectHeader = ({ project, section, isPublished, isAuthor, setPublish, togglePublish, contextualCommentsCount, contributionsCount, contributorsCount, currentSection, withComments, apoyarProyecto }) => ( +const SharerButton = styled(ModeBarLinkButton)` +color:#567B9A; +` +const SharerSpan = styled.span` +font-family: var(--bold); +margin-right:8px +` + +const ProjectHeader = ({ project, section, isPublished, isAuthor, setPublish, togglePublish, contextualCommentsCount, contributionsCount, contributorsCount, currentSection, withComments, apoyarProyecto }) => { + const childSuportRef = useRef() + const childSharedRef = useRef() + const toogleform = (element)=> { + if (element.apoyarProyecto) { + childSharedRef.current.close() + } else { + childSuportRef.current.close() + } + } + + return( // - - - - - - - - - - - {isAuthor && - + + + + + + {/* */} + + + + + + {isAuthor && + + } + + {/* */} + {project.currentVersion.content.title} + + + + + + {/* */} + {isAuthor && + + } + {project.closed && + + } + + {currentSection === '/propuesta' && + +
+ Presentación + Artículos +
+ + {/* + + Compartir proyecto + + + */} + + + +
} -
- - {project.currentVersion.content.title} - - {isAuthor && - - } - {project.closed && - - } - {currentSection === '/propuesta' && - - Presentación - Artículos - - - } - {currentSection === '/versiones' && - - Presentación - Artículos - - - } - {currentSection === '/articulado' && - - Presentación - Artículos - - - {withComments ? : }  - Modo lectura - - - } -
-
-) + {currentSection === '/versiones' && + +
+ Presentación + Artículos +
+ + + {/* + + Compartir proyecto + + + */} + + + + +
+ } + {currentSection === '/articulado' && + +
+ Presentación + Artículos + + {withComments ? : }  + Modo lectura + + +
+ + {/* + + Compartir proyecto + + + */} + + + +
+ } + +
+ ) +} ProjectHeader.propTypes = { project: PropTypes.object.isRequired, diff --git a/components/project-table-item/component.js b/components/project-table-item/component.js index 9d572072..5fff6345 100644 --- a/components/project-table-item/component.js +++ b/components/project-table-item/component.js @@ -101,10 +101,14 @@ export default ({ project }) => ( Fecha de cierre: {formatDate(project.currentVersion.content.closingDate)} + {project.closed ? : }  {project.closed ? 'Cerrado' : 'Abierto'} {project.published ? : }  {project.published ? 'Publico' : 'Oculto'} + {window.location.pathname === '/admin' && + {project.author.fullname} + }

{project.commentsCount} Aport{project.commentsCount > 1 ? 'es' : 'e'} diff --git a/components/project-tags/component.js b/components/project-tags/component.js index cb71111e..70b3c228 100644 --- a/components/project-tags/component.js +++ b/components/project-tags/component.js @@ -2,7 +2,8 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import styled from 'styled-components' import { ArticlesContext } from '../../containers/user-project-container/component' -import WithDocumentTagsContext from '../../components/document-tags-context/component' +import WithDocumentTagsContext from '../document-tags-context/component' +import ProjectTag from '../../elements/project-tag/component' const ProjectTagsContainer = styled.div` // min-height: 383px; @@ -21,13 +22,7 @@ const ProjectTagsContainer = styled.div` } ` -const ProjectTag = styled.span` - font-size: 15px; - background-color: #eee; - padding: 8px 15px; - margin-bottom: 5px; - margin-right: 5px; -` + class ProjectTags extends Component { @@ -55,7 +50,7 @@ class ProjectTags extends Component { ({ isAuthor, editMode, setYoutubeId, editedYoutubeId, newYoutubeId, setNewFields }) => ( projectTags.map(tagId => - { allTags.find(documentTag => documentTag._id == tagId).name } + { allTags.find(documentTag => documentTag._id == tagId) && allTags.find(documentTag => documentTag._id == tagId).name } ) ) diff --git a/components/sobre-el-sitio/component.js b/components/sobre-el-sitio/component.js index c40ff278..a703300b 100644 --- a/components/sobre-el-sitio/component.js +++ b/components/sobre-el-sitio/component.js @@ -12,10 +12,7 @@ export default () => ( Sobre el sitio - El Portal de Leyes Abiertas es un desarrollo basado en la tecnología provista por Democracia en Red, coordinado por el Programa de Modernización parlamentaria de la Cámara de Diputados de La Nación. - - - Esta es una versión “Beta”, aún en evaluación y desarrollo. Como tal, puede tener errores que pueden ser reportados a leyesabiertas@hcdn.gob.ar. + El Portal de Leyes Abiertas está desarrollado por Democracia en Red y coordinado por la Dirección General de Innovación, Planificación y Nuevas Tecnologías de la Cámara de Diputados de La Nación.

diff --git a/components/sobre-la-plataforma/component.js b/components/sobre-la-plataforma/component.js index aaadee07..02f7f9b7 100644 --- a/components/sobre-la-plataforma/component.js +++ b/components/sobre-la-plataforma/component.js @@ -16,7 +16,7 @@ export default () => ( ¿Qué es? - El Portal de Leyes Abiertas es una plataforma de elaboración colaborativa de normas donde los diputados ponen a disposición de la ciudadanía sus propuestas y proyectos de ley para incorporar nuevos puntos de vista a sus iniciativas. El objetivo de la plataforma es enriquecer las propuestas de ley y generar un nuevo espacio de comunicación con los ciudadanos, que permita enriquecer el debate parlamentario. + El Portal de Leyes Abiertas es una plataforma de elaboración colaborativa de normas donde las y los diputados ponen a disposición de la ciudadanía sus propuestas y proyectos de ley para incorporar nuevos puntos de vista a sus iniciativas. El objetivo de la plataforma es enriquecer las propuestas de ley y generar un nuevo espacio de comunicación con la ciudadanía, que permita enriquecer el debate parlamentario.
@@ -27,15 +27,15 @@ export default () => ( Según la Constitución la redacción y discusión de la ley debe darse en el ámbito del Congreso. - Esta herramienta nace del enfoque de parlamento abierto, el cual promueve la incorporación de los ciudadanos en los procesos de toma de decisiones públicas. + Esta herramienta nace del enfoque de parlamento abierto, el cual promueve la incorporación de la ciudadanía en los procesos de toma de decisiones públicas. - ¿Qué es la co-creación de propuestas de ley? + ¿Qué es la cocreación de propuestas de ley? - La co-creación de propuestas de ley rompe con la forma tradicional de legislar donde un equipo de políticos, profesionales y técnicos elaboran los proyectos de ley. Co-crear supone abrir y federalizar ese proceso y crear un espacio en el que se encuentran legisladores con la ciudadanía, la academia, las organizaciones de la sociedad civil y personas especializadas en las temáticas que se están discutiendo. + La cocreación de propuestas de ley rompe con la forma tradicional de legislar donde un equipo de políticos, profesionales y técnicos elaboran los proyectos de ley. Cocrear supone abrir y federalizar ese proceso y crear un espacio en el que se encuentran las y los legisladores con la ciudadanía, la academia, las organizaciones de la sociedad civil y personas especializadas en las temáticas que se están discutiendo. diff --git a/components/static-info/component.js b/components/static-info/component.js index 842709fc..2739b417 100644 --- a/components/static-info/component.js +++ b/components/static-info/component.js @@ -46,13 +46,26 @@ const content = { const StyledStaticInfo = styled.div` display: flex; justify-content: center; - background-image: url('/static/assets/header-background.jpg'); + background-image: ${(props)=> `url(${props.backgroundImg})`}; background-repeat: no-repeat; - background-size: cover; + background-size: 100% auto; ` +const selectBackground = (section)=>{ + switch (section){ + case 'como-participar': + return '/static/assets/images/como_participar.jpg' + case 'sobre-el-sitio': + return '/static/assets/images/sobre_el_sitio.jpg' + case 'faq': + return '/static/assets/images/preguntas_frecuentes.jpg' + default: + return '/static/assets/images/foto_acerca_de.jpg' + } +} + const StaticInfo = (props) => ( - + {buttons.map((button, i) => ( diff --git a/components/terminos-y-condiciones/component.js b/components/terminos-y-condiciones/component.js index e045d82a..55824599 100644 --- a/components/terminos-y-condiciones/component.js +++ b/components/terminos-y-condiciones/component.js @@ -12,7 +12,6 @@ import StaticInfoButton from '../../elements/static-info-button/component' const StyledStaticInfo = styled.div` display: flex; justify-content: center; - background-image: url('/static/assets/header-background.jpg'); background-repeat: no-repeat; ` const buttons = [ @@ -32,7 +31,7 @@ const scroll = (target) => (e) => { export default () => ( - +
Términos y condiciones diff --git a/components/title-content-admin/component.js b/components/title-content-admin/component.js new file mode 100644 index 00000000..83b5c3d7 --- /dev/null +++ b/components/title-content-admin/component.js @@ -0,0 +1,14 @@ +import styled from 'styled-components' + + +const TitleContent = styled.div` +font-size: 5.5rem; + font-family: var(--bold); + line-height: 1.18rem; + color: #203340; + margin-top: 23px; + margin-bottom: 80px; + text-transform:capitalize +` + +export default TitleContent \ No newline at end of file diff --git a/components/tooltip/component.js b/components/tooltip/component.js index 051e483f..5e3f3c58 100644 --- a/components/tooltip/component.js +++ b/components/tooltip/component.js @@ -25,7 +25,7 @@ const StyledTooltip = styled.div` position: fixed; top: ${(props) => props.top}; right: ${(props) => props.right}; - z-index: 9; + z-index: 200; animation-name: ${transition}; animation-duration: 2s; animation-iteration-count: infinite; diff --git a/components/user-bar/component.js b/components/user-bar/component.js index 14eafd15..7c3a1fce 100644 --- a/components/user-bar/component.js +++ b/components/user-bar/component.js @@ -6,8 +6,14 @@ import styled from 'styled-components' const Bar = styled.div` display:flex; justify-content:flex-end; -width:33%; margin-top:auto; +margin-bottom:auto; +@media(max-width:760px){ + justify-content: start; + border-bottom: 1px solid #D6D6D6; + width:100%; + + } ` const UserBar = ({ children }) => ( diff --git a/components/user-context/component.js b/components/user-context/component.js index cd94f3ea..562561b0 100644 --- a/components/user-context/component.js +++ b/components/user-context/component.js @@ -4,7 +4,8 @@ const UserContext = React.createContext({ keycloak: null, authenticated: false, login: null, - isAuthor: null + isAuthor: null, + isAdmin: null }) export default UserContext diff --git a/components/user-menu/component.js b/components/user-menu/component.js index c6235d30..2694a41c 100644 --- a/components/user-menu/component.js +++ b/components/user-menu/component.js @@ -4,11 +4,10 @@ import fetch from 'isomorphic-unfetch' import styled from 'styled-components' const StyledUl = styled.ul` - height: ${(props) => props.isAuthor ? '154px' : '104px'}; width: 200px; @media (max-width: 760px) { width: 150px; - height: 74px; + } box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.05); border: solid 1px #e9e9e9; @@ -73,7 +72,7 @@ const StyledA = styled.span` ` -const Usermenu = ({ logout }) => ( +const Usermenu = ({ logout,user }) => (
  • @@ -83,6 +82,13 @@ const Usermenu = ({ logout }) => (
  • + {user && user.roles.includes('admin') &&
  • + + + Admin + + +
  • } {/* { isAuthor &&
  • Nueva propuesta @@ -91,6 +97,7 @@ const Usermenu = ({ logout }) => (
  • Cerrar sesión
  • +
    ) diff --git a/containers/about/component.js b/containers/about/component.js index 3dbd6020..e89bb878 100644 --- a/containers/about/component.js +++ b/containers/about/component.js @@ -8,8 +8,7 @@ import AboutP from '../../elements/about-p/component' import ArrowRightLink from '../../elements/arrow-right-link/component' const StyledAbout = styled.div` - height:540px; - width:90%; + height:450px; @media(max-width:700px){ flex-direction: column; } @@ -20,13 +19,16 @@ const StyledAbout = styled.div` box-sizing: border-box; } ` +const AboutTitleBaner = styled(AboutH2)` +color:#4C7394; +` const About = () => ( - + - Acerca de - ¿Qué es? El Portal de Leyes Abiertas es una plataforma de elaboración colaborativa de normas, donde las y los diputados abren a debate sus iniciativas para incorporar puntos de vista ciudadanos. + Acerca de + El Portal de Leyes Abiertas es una plataforma de elaboración colaborativa de normas, donde las y los diputados abren a debate sus iniciativas para incorporar puntos de vista ciudadanos. diff --git a/containers/admin-section/component.js b/containers/admin-section/component.js new file mode 100644 index 00000000..5607b309 --- /dev/null +++ b/containers/admin-section/component.js @@ -0,0 +1,45 @@ +import React from 'react' +import styled from 'styled-components' +import PropTypes from 'prop-types' +import TagsAdmin from '../../components/admin-tags/component' +import ProjectsAdmin from '../../components/admin-projects/component' +import UsersAdmin from '../../components/admin-users/component' +import UserEdit from '../../components/admin-edit-user/component' + +const StyledAdminSection = styled.div` +width:70%; +margin-left:auto; +margin-right:auto; +margin-top:2.5rem; +margin-bottom:2.5rem; +display:flex; +padding:16px; +@media (max-width: 700px) { + width:100%; + margin-top:1.5rem; + +} + +` + + + +const AdminSection = (props) => { + const content = { + 'tags':, + 'projects':, + 'users':, + 'userEdit': + + } + return( + + {content[props.section]} + +)} + +AdminSection.propTypes = { + section:PropTypes.string +} + +export default AdminSection diff --git a/containers/app-wrapper/component.js b/containers/app-wrapper/component.js index d40f0ede..7dddfe30 100644 --- a/containers/app-wrapper/component.js +++ b/containers/app-wrapper/component.js @@ -81,16 +81,18 @@ export default class extends Component { Keycloak = require('keycloak-js') const keycloak = await Keycloak(keycloakOptions) try { - const authenticated = await keycloak.init({ onLoad: 'check-sso', promiseType: 'native' }) + const authenticated = await keycloak.init({ onLoad: 'check-sso', promiseType: 'native', checkLoginIframe: false }) const isAuthor = authenticated ? await keycloak.hasRealmRole('accountable') : false const profile = authenticated && await keycloak.loadUserInfo() const user = authenticated ? await this.fetchMe(keycloak.token) : null + const isAdmin =authenticated ? await keycloak.hasRealmRole('admin') : false this.setState({ keycloak: keycloak, authenticated: authenticated, isAuthor: isAuthor, profile: profile, user: user, + isAdmin, login: keycloak.login, register: keycloak.register, logout: keycloak.logout diff --git a/containers/general-container/component.js b/containers/general-container/component.js index 63d13f4d..c4ec450c 100644 --- a/containers/general-container/component.js +++ b/containers/general-container/component.js @@ -20,17 +20,20 @@ const Wrapper = styled.div` class GeneralContainer extends Component { state = { - project: null + project: null, + isAdmin:null } componentDidMount () { if (!this.props.authContext.keycloak) return + if (this.props.authContext.user.roles.includes('admin')) {this.setState({isAdmin: true})} this.fetchDocument(this.props.project, this.props.authContext.keycloak.token) } componentWillUpdate (props) { if (!props.authContext.keycloak) return if (props === this.props) return + if (props.authContext.authenticated && props.authContext.user.roles.includes('admin')) {this.setState({isAdmin: true})} this.fetchDocument(props.project, props.authContext.keycloak.token) } @@ -95,17 +98,19 @@ class GeneralContainer extends Component { } render () { + const {isAdmin} = this.state return (
    - + {/* */}