diff --git a/.firebaserc b/.firebaserc new file mode 100644 index 00000000..d76693e3 --- /dev/null +++ b/.firebaserc @@ -0,0 +1,5 @@ +{ + "projects": { + "default": "social-nerwork" + } +} diff --git a/Readme.md b/Readme.md index ed84ec5c..46369e17 100644 --- a/Readme.md +++ b/Readme.md @@ -1,167 +1,127 @@ -# Creando una Red Social +# :book: estuDÍA -Bienvenida a tu primer proyecto del track de Frontend en Laboratoria. - -En este proyecto construirás una red social, cuya temáticas dejaremos a tu elección. - -Aquí algunas ideas para inspirarte: - -- Alimentación -- Feminismo -- Educación -- Salud -- Energías Renovables - -Las **caraterísticas técnicas** de tu aplicación serán las siguientes: -- Debe ser una Single-Page Application [SPA](https://dzone.com/articles/how-single-page-web-applications-actually-work) ([versión traducida](https://dzone.com/articles/how-single-page-web-applications-actually-work)) -- Debe ser diseñada con un enfoque [mobile first](https://darwindigital.com/mobile-first-versus-responsive-web-design/) ([versión traducida](https://translate.google.com/translate?hl=&sl=auto&tl=es&u=https%3A%2F%2Fdarwindigital.com%2Fmobile-first-versus-responsive-web-design)) -- Debe permitir la persintencia de datos +The objective of this project is to build a responsive Single-Page Application (SPA) Social Network in which we can write, read, update and delete data. It is the first project on the Laboratory FrontEnd track. -Para implementar tu aplicación usarás *HTML5*, *CCS3* o *SASS*, *Vanilla JavaScript(ES6+)*, *Firebase* o *LocalStogarge* - -## Objetivo - -El objetivo de este proyecto es construir una Red Social, Single-Page Application (SPA), responsiva en la que podamos escribir, leer, actualizar y eliminar datos. - -Para ello deberás poner en juego tu creatividad para generar ideas que lleven a una solución original y valiosa del problema, trabajando en equipo buscando feedback constante. - -En otras palabras, seguirás afianzando todo lo aprendido en el Common Core, pero en particular verás : -### Planificación -* Te recomendamos utilizar *projects*, *issues* y *milestones* de GitHub para gestionar la planificación de tu proyecto. Estos recursos serán la fuente de organización de tu equipo y a través de estas herramientas tus coaches podrán ver el avance del proyecto y darte feedback. +### :bulb: IDEA SELECTION +The chosen theme was education. **estuDÍA** is a social network in which users have an interest in entering their university studies specifically at the Instituto Politécnico Nacional. -* Escribir, de manera colaborativa, las **Definiciones de terminado** y **Criterios de Aceptación** por cada **Historia de usuario** que te daremos para este proyecto y que se deberán ver reflejadas en tu planificación. +### :girl: :boy: USERS -* **Priorizar** la implementación de tus funcionalidades, en función al esfuerzo que demandan en relación al valor que le aportan al usuario, y ejecutar en equipo todas las historias de usuario dentro del tiempo estimado para cada sprint y que finalmente se vean reflejadas en publicaciones completamentamente funcionales al final de cada sprint. +**estuDÍa** is aimed at all people regardless of age, sex or occupation who want to pursue higher level studies at the Instituto Politécnico Nacional. -* Adquirir la disciplina de la completitud, terminando una historia de usuario antes de pasar a la siguiente (es decir, que cumple con *Definición de Terminado* y *Criterios de Aceptación* contemplando todos los puntos que son objetivos de aprendizaje para este proyecto). -### Desarrollo frontend +### :rotating_light: PROBLEM -#### Arquitectura de la aplicación +In 2019, of the 92 thousand applicants who competed to obtain a place at the top level of the IPN, only 26%, equivalent to 24 thousand young people, had the opportunity to study in one of their 66 careers, in school or non-school modality. -- Diseñar la arquitectura de tu aplicación, modularizando tu código a través de *es modules* ([`import`](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Sentencias/import) y [`export`](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Sentencias/export)) +In Mexico, educational quality is poor and is not standardized in schools. In the case of upper secondary education, the study plans vary from one educational institution to another, for example, the study plan of a CONALEP and a COBACH. they are the same and the educational gap is even more pronounced in schools in the interior of the republic. -- Familiarizarte con el patrón modelo - vista - controlador ([MVC](https://es.wikipedia.org/wiki/Modelo%E2%80%93vista%E2%80%93controlador)). +The UNAM and the IPN two of the best universities in the country have their own upper-middle-level institutions (CCH, ENP and CECYT) in which the study plans are designed so that their graduates are able to take and pass their respective exams admission for higher education. However, many students from the interior of the republic, or from institutions with little educational quality, face a real challenge when it comes to studying and understanding subjects for entrance exams to the IPN or UNAM. -#### Tecnologías HTML5 y CSS3/SASS -* Aplicar HTML5 semántico en tu proyecto. -* Aplicar y reforzar los conceptos fundamentales de CSS3. -* Implementar selectores de clase evitando la redundancia de estilos en CCS3. -* Utilizar `flexbox` para lograr un diseño `mobile first`, implementando un layout que te permita crear un diseño adaptativo para **mobile y desktop** +### :computer: WHAT DO WE PROPOSE? -A continuación te proporcionamos el layout (diseño) de la vista mobile y desktop que deberás replicar visualmente y cuyo contenido, colores y fuentes de texto, dejaremos a tu elección. +**estuDÍA** is created with the aim of creating a support network so that people interested in carrying out the admission process to the IPN, is a space where users through publications can share, tips, experiences And anything that they think can help others to take a selection exam and start the university stage and thus make the process easier and more solid among applicants. -* Vista mobile +### :hourglass_flowing_sand: TO WORK! - ![mobile](https://user-images.githubusercontent.com/32286663/56174616-ec9f6100-5fb8-11e9-9edb-d5ef7c251d9c.png) +In order to have a homogeneous distribution of the tasks to be carried out, to be informed of the activities, to contribute ideas, we relied on the GitHub "Pojects" tool, so our SCRUM could be consulted and modified by any of the three team members. -* Vista Desktop + - ![desktop](https://user-images.githubusercontent.com/32286663/56174626-fcb74080-5fb8-11e9-8854-26e8d9c4e25f.png) -#### JavaScript (ES6+) +### :bust_in_silhouette: USER STORIES -* Utilizar modulos de ES6 para poder modularizar tu código JavaScript. -* Reforzar tu conocimiento sobre el uso de Template literals (Template strings). -* Reforzar tu conocimiento de la manipulacion de los elementos del DOM a traves JavaScript. -* Implementar un sistema de rutas para cambiar de una vista a otra de manera dinámica (SPA). -* Testear la lógica de tu aplicación, con Jest cuidando que el coverage pase el 90% de statements (sentencias), functions (funciones), lines (líneas), y branches (ramas). +:one: +**ME** as a new user. +**I WANT** to be able to create an account with valid email and password. +**TO** be able to log in and enter the social network. -### Persistencia de datos +:two: +**ME** as a new user +**I WANT** to have the option to log in with my Google or Facebook account +**TO** enter the social network without creating a valid email account -En los proyectos anteriores solo has consumido (leído) datos, por ejemplo, a través de un archivo `json` o utilizando `fetch`. +:three: +**ME** as a logged in user +**I WANT** to create, save, modify in the same place (in place) and delete a private or public (post) publication, which can be a phrase or an image -En este proyecto diseñarás la estructura de esos datos, la forma de consultarlos, actualizarlos, modificarlos y eliminarlos según los requerimiento del usuario. Para llevarlo a cabo utilizaras `Firestore` de `Firebase` o `LocalStogarge` una `Web Storage API` +:four: +**ME** As a logged in user +**I WANT** to be able to see all the public and private posts I have created so far, from the most recent to the oldest +**TO** be able to change the privacy settings of my post. -#### Firebase +:five: +**ME** as a logged in user +**I WANT** to like and keep a count of likes in my publication as well as being able to write, save, edit or delete a comment in my publication -El objetivo de usar Firestore en este proyecto, es que aprendes a manejar y persistir datos a traves de una base de datos no relacional, en tiempo real y puedas implementar operaciones CRUD (Creación, Lectura, Actualización y eliminación) de datos. +:six: +**ME** as a logged in user +**I WANT** to be able to enter the social network +**TO** be able to view the data of my created profile or edit it. -## Consideraciones generales del proyecto -* Este proyecto debe ser desarrollado en equipos de trabajo de 3 integrantes. +### :heavy_check_mark: CRITERIA OF ACCEPTANCE -* La duración propuesta del proyecto es **3 sprints**, con una duración de una semana cada uno. +-If the email or password is not valid, when logging in, I should be able to see an error message. +-It should be visible if there is any error message. +-I must be able to see this creation page on Mobile and desktop (responsive). +-You do not need to reload the page to create an account (SPA). +-I must be able to publish text and images. +-The user must be able to filter, edit and delete their publications. -* Te daremos las **historias de usuario** con el fin de presentarte los requerimientos y funcionalidades que desea el usuario final. -* La **planificación es vital**, para ello te recomendamos utilizar el flujo de trabajo colaborativo que nos ofrecen los projects de GitHub, para que puedas **escribir tus definiciones de terminado** y **criterios de aceptación** por cada historia de usuario con el objetivo que determinen, en equipo, **qué hacer** en el sprint y **cómo se realizará**. +### :heavy_check_mark: DEFINITION OF FINISHING -* Para que todas las miembros de tu equipo puedan lograr los objetivos de aprendizaje, deberán determinar cuál será la estrategia de desarrollo que utilizarán: división por sub-historias, pair programming, code reviews, etc. +-The functionality meets satisfies the acceptance criteria. +-The functionality has unit tests. +-The visual design corresponds to the high fidelity prototype.- +-The code of this functionality received code review. +-The functionality is deployed and public to be tested. +-The functionality was manually tested. +-Utility tests were carried out and feedback was implemented if deemed necessary. -## Restricciones Técnicas -* Debes utilizar `flexbox` o `CSSGrid` para posicionar tus elementos. Si está permitido el uso de frameworks de CCS (bootstrap), y de componentes flotantes. +### :vhs: LOW-FIDELITY PROTOTYPE -* Ya te damos un diseño (layout) de la vista mobile y desktop, queremos que lo repliques a nivel *pixel perfect*, el contenido, paleta de colores y fuentes, depende de la temática que van a elegir como equipo. La implementación de ese layout deberá formar parte de la *definición de terminado* de tus historias de usuario. +During this stage we designed the user interface, we thought the name of our web application, the distribution of the elements to consult and how it would look on a cell phone screen. -* Los test son **fundamentales** y deberán formar parte de tu definición de terminado, te recomendamos que comiences a implementarlos desde el comienzo. + + -## Historias de Usuario +We sought feedback five times from our bootcamp partners. -* Como usuario nuevo debo poder crear una cuenta con email y password válidos para poder iniciar sesion e ingresar a la red social. +### :iphone: HIGH-FIDELITY PROTOTYPE -* Como usuario nuevo debo poder tener la opción de iniciar sesión con mi cuenta de Google o Facebook para ingresar a la red social sin necesidad de crear una cuenta de email válido. +To develop this part we apply the feedback that our coach and our colleagues gave us, we also take into account the official colors of the National Polytechnic Institute: cherry and white. The hi-fi prototype was developed in Figma. -* Como usuario loggeado debo poder crear, guardar, modificar en el mismo lugar (in place) y eliminar una publicación (post) privada o pública, que puede ser una frase o una imagen. + + + + -* Como usuario loggeado debo poder ver todos los posts públicos y privados que he creado hasta ese momento, desde el más reciente hasta el más antiguo, así como la opción de poder cambiar la configuración de privacidad de mis post. - -* Yo como usuario loggeado, puedo dar like y llevar un conteo de likes en mi publicación así como poder escribir, guardar, editar o eliminar un comentario en mi publicación. - -* Al final debo poder ingresar a la red social y poder visualizar los datos de mi perfil creado o editarlos. - -* Te dejamos un ejemplo de cómo definir criterios de aceptación y definiciones de terminado para una H.U. Si se te complica definirlas o no tienes idea de que considerar para cada H.U. es de gran ayuda revisar redes sociales como `facebok`, `twitter`, `instagram`, `devopedia` o la red social que más te guste y puedas evaluar qué consideran en cada funcionalidad para darla como terminada y aceptada. - - > Como usuario nuevo debo poder crear una cuenta con email y password para - > poder iniciar sesion. Por otro lado, necesito también tener la opción de - > iniciar sesión con mi cuenta de Google o Facebook. - > - > **Criterios de aceptación** - > - Si el mail o password no es válido, al momento de logearme, debo poder - > ver un mensaje de error. - > - Debe ser visible si hay algún mensaje de error. - > - Debo poder ver esta página de creación en Móviles y desktop (responsive). - > - No debe necesitar recargar la página para crear una cuenta (SPA). - > - > **Definición de terminado** - > - La funcionalidad cumple satisface los criterios de aceptación. - > - La funcionalidad tiene _test unitarios_. - > - El diseño visual corresponde al prototipo de alta fidelidad. - > - El código de esta funcionalidad recibió code review. - > - La funcionalidad esta desplegada y pública para ser probada. - > - La funcionalidad fue probada manualmente. - > - Se hicieron pruebas de usuabilidad y se implementó el feedback si se - > consideró necesario. - -## Objetivos de aprendizaje - -Antes de empezar el proyecto, recuerda agregar tus objetivos de aprendizaje pendientes de tu proyecto -anterior en la siguiente sección. - -### Objetivos de aprendizajes pendientes +### :pencil: LEARNING OBJECTIVES ### HTML y CSS -* [ ] [HTML semántico](https://developer.mozilla.org/en-US/docs/Glossary/Semantics#Semantics_in_HTML) +* [ ] [semantic HTML](https://developer.mozilla.org/en-US/docs/Glossary/Semantics#Semantics_in_HTML) * [ ] [CSS `flexbox`](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) -* [ ] Construir tu aplicación respetando el diseño realizado (maquetación). +* [ ] Build your application respecting the design made (layout). ### DOM y Web APIs -* [ ] [Manipulación dinámica del DOM](https://developer.mozilla.org/es/docs/Referencia_DOM_de_Gecko/Introducci%C3%B3n) +* [ ] [Dynamic DOM manipulation](https://developer.mozilla.org/es/docs/Referencia_DOM_de_Gecko/Introducci%C3%B3n) * [ ] [History API](https://developer.mozilla.org/es/docs/DOM/Manipulando_el_historial_del_navegador) * [ ] [`localStorage`] ### Javascript -* [ ] [Uso de callbacks](https://developer.mozilla.org/es/docs/Glossary/Callback_function) -* [ ] [Consumo de Promesas](https://scotch.io/tutorials/javascript-promises-for-dummies#toc-consuming-promises) -* [ ] Uso ES modules +* [ ] [Use of callbacks](https://developer.mozilla.org/es/docs/Glossary/Callback_function) +* [ ] [Consumption of Promises](https://scotch.io/tutorials/javascript-promises-for-dummies#toc-consuming-promises) +* [ ] ES modules ([`import`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) | [`export`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export)) @@ -170,103 +130,32 @@ anterior en la siguiente sección. * [ ] [Firestore](https://firebase.google.com/docs/firestore) * [ ] [Firebase Auth](https://firebase.google.com/docs/auth/web/start) * [ ] [Firebase security rules](https://firebase.google.com/docs/rules) -* [ ] [Uso de onSnapshot](https://firebase.google.com/docs/firestore/query-data/listen) +* [ ] [Use of onSnapshot](https://firebase.google.com/docs/firestore/query-data/listen) | [onAuthStateChanged](https://firebase.google.com/docs/auth/web/start#set_an_authentication_state_observer_and_get_user_data) ### Testing -* [ ] [Testeo de tus funciones](https://jestjs.io/docs/es-ES/getting-started) -* [ ] [Testeo asíncrono](https://jestjs.io/docs/es-ES/asynchronous) +* [ ] [Testing your functions](https://jestjs.io/docs/es-ES/getting-started) +* [ ] [Asynchronous testing](https://jestjs.io/docs/es-ES/asynchronous) * [ ] [Mocking](https://jestjs.io/docs/es-ES/manual-mocks) -### Colaboración en Github +### Collaboration on Github * [ ] Branches * [ ] Pull Requests * [ ] Tags -### Organización en Github +### Organization on Github * [ ] Projects * [ ] Issues * [ ] Labels * [ ] Milestones -### Buenas prácticas de desarrollo +### Good development practices -* [ ] Modularización -* [ ] Nomenclatura / Semántica +* [ ] Modularization +* [ ] Nomenclature / Semantics * [ ] Linting -*** - -## Recursos - -### Mobile first - -El concepto de [_mobile first_](https://www.mediaclick.es/blog/diseno-web-responsive-design-y-la-importancia-del-mobile-first/) -hace referencia a un proceso de diseño y desarrollo donde partimos de cómo se ve -y cómo funciona la aplicación en un dispositivo móvil primero, y más adelante se -ve como adaptar la aplicación a pantallas progresivamente grandes y -características específicas del entorno desktop. Esto es en contraposición al -modelo tradicional, donde primero se diseñaban los websites (o webapps) para -desktop y después se trataba de _arrugar_ el diseño para que entre en pantallas -más chicas. La clave acá es asegurarse de que desde el principio diseñan usando -la vista _responsive_ de las herramientas de desarrollador (developer tools) del -navegador. De esa forma, partimos de cómo se ve y comporta la aplicación en una -pantalla y entorno móvil. - -### Múltiples vistas - -En proyectos anteriores nuestras aplicaciones habían estado compuestas de una -sola _vista_ principal (una sóla _página_). En este proyecto se introduce la -necesidad de tener que dividir nuestra interfaz en varias _vistas_ o _páginas_ -y ofrecer una manera de navegar entre ellas. - -### Escritura de datos - -En los proyectos anteriores hemos consumido (leído) datos, pero todavía no -habíamos escrito datos (salvar cambios, crear datos, borrar, ...). En este -proyecto tendrás que crear (salvar) nuevos datos, así como leer, actualizar y -modificar datos existentes. Estos datos se podrán guardar de forma remota -usando [Firestore](https://firebase.google.com/docs/firestore) o de forma -local utilizando`localStorage`. - -### Autenticación y autorización - -Hasta el momento, los proyectos han sido pensados como recursos públicos, donde todos -los usuarios compartían un mismo rol y la misma información. - -En este proyecto tendrás que diferenciar la información a mostrar y modificar, -dependiendo de la identidad del usuario. -De la misma manera deberás crear reglar de autorización para el acceso a los -datos. - -Para esto utilizaras respectivamente -[`Firebase authentication`](https://firebase.google.com/docs/auth/) y -[`Firestore security rules`](https://firebase.google.com/docs/firestore/security/get-started) - -### CSS - -En este proyecto queremos que ganes confianza y experiencia con CSS profesional, -por eso usarás [`flexbox`](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) -para posicionar tus elementos. - -Recuerda que no puedes usar frameworks CSS, sólo *vanilla css* o [*sass*](https://sass-lang.com/). - -### Otras: - -* [Pildora SPA](https://www.loom.com/share/fa63a8ad0e9a43428222c15b6f6613d3) -* [Repositorio de pildora de SPA](https://github.com/betsyvies/bootcamp-spa) -* [Pildora de mock Firebase](https://www.youtube.com/watch?v=06myVn41OTY&t=1s) -* [Repositorio de pildora de mock Firebase](https://github.com/Danielalab/2018-2-Testing) -* [Pildora MVC](https://github.com/merunga/todomvc-vanillajs) -* [Modulos: Export](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Sentencias/export) -* [Modulos: Import](https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Sentencias/import) -* [Diseño web, responsive design y la importancia del mobile first - Media Click](https://www.mediaclick.es/blog/diseno-web-responsive-design-y-la-importancia-del-mobile-first/) -* [Mobile First: el enfoque actual del diseño web móvil - 1and1](https://www.1and1.es/digitalguide/paginas-web/diseno-web/mobile-first-la-nueva-tendencia-del-diseno-web/) -* [Mobile First - desarrolloweb.com](https://desarrolloweb.com/articulos/mobile-first-responsive.html) -* [Mobile First - ZURB](https://zurb.com/word/mobile-first) -* [Mobile First Is NOT Mobile Only - Nielsen Norman Group](https://www.nngroup.com/articles/mobile-first-not-mobile-only/) -*** diff --git a/database.rules.json b/database.rules.json new file mode 100644 index 00000000..f54493db --- /dev/null +++ b/database.rules.json @@ -0,0 +1,7 @@ +{ + /* Visit https://firebase.google.com/docs/database/security to learn more about security rules. */ + "rules": { + ".read": false, + ".write": false + } +} \ No newline at end of file diff --git a/docs/.eslintrc.js b/docs/.eslintrc.js new file mode 100644 index 00000000..5f656948 --- /dev/null +++ b/docs/.eslintrc.js @@ -0,0 +1,17 @@ +module.exports = { + "env": { + "es6": true, + "node": true + }, + "extends": "eslint:recommended", + "globals": { + "Atomics": "readonly", + "SharedArrayBuffer": "readonly" + }, + "parserOptions": { + "ecmaVersion": 2018, + "sourceType": "module" + }, + "rules": { + } +}; \ No newline at end of file diff --git a/docs/img/Group.svg b/docs/img/Group.svg new file mode 100644 index 00000000..28feeabc --- /dev/null +++ b/docs/img/Group.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/docs/img/LogoIPN.png b/docs/img/LogoIPN.png new file mode 100644 index 00000000..a064b50e Binary files /dev/null and b/docs/img/LogoIPN.png differ diff --git a/docs/img/convocatoria.png b/docs/img/convocatoria.png new file mode 100644 index 00000000..ae47164d Binary files /dev/null and b/docs/img/convocatoria.png differ diff --git a/docs/img/demanda.png b/docs/img/demanda.png new file mode 100644 index 00000000..059401f3 Binary files /dev/null and b/docs/img/demanda.png differ diff --git a/docs/img/estudia.png b/docs/img/estudia.png new file mode 100644 index 00000000..ad257d92 Binary files /dev/null and b/docs/img/estudia.png differ diff --git a/docs/img/facebook.svg b/docs/img/facebook.svg new file mode 100644 index 00000000..12ecbe1d --- /dev/null +++ b/docs/img/facebook.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/img/gmail.svg b/docs/img/gmail.svg new file mode 100644 index 00000000..f99ed6eb --- /dev/null +++ b/docs/img/gmail.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/img/guia.png b/docs/img/guia.png new file mode 100644 index 00000000..cff145f4 Binary files /dev/null and b/docs/img/guia.png differ diff --git a/docs/img/img.svg b/docs/img/img.svg new file mode 100644 index 00000000..790de8c1 --- /dev/null +++ b/docs/img/img.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/img/info.svg b/docs/img/info.svg new file mode 100644 index 00000000..3a5019c6 --- /dev/null +++ b/docs/img/info.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/docs/img/inicioSesion.png b/docs/img/inicioSesion.png new file mode 100644 index 00000000..9fda8d9a Binary files /dev/null and b/docs/img/inicioSesion.png differ diff --git a/docs/img/init.jpg b/docs/img/init.jpg new file mode 100644 index 00000000..b9b0c5fd Binary files /dev/null and b/docs/img/init.jpg differ diff --git a/docs/img/init0.jpg b/docs/img/init0.jpg new file mode 100644 index 00000000..e718d930 Binary files /dev/null and b/docs/img/init0.jpg differ diff --git a/docs/img/like.svg b/docs/img/like.svg new file mode 100644 index 00000000..8bc4a3cf --- /dev/null +++ b/docs/img/like.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/img/logo-ipn-blanco.png b/docs/img/logo-ipn-blanco.png new file mode 100644 index 00000000..8ec3d5c6 Binary files /dev/null and b/docs/img/logo-ipn-blanco.png differ diff --git a/docs/img/modal.png b/docs/img/modal.png new file mode 100644 index 00000000..aa35564f Binary files /dev/null and b/docs/img/modal.png differ diff --git a/docs/img/noImgIpn.png b/docs/img/noImgIpn.png new file mode 100644 index 00000000..f5ff1f99 Binary files /dev/null and b/docs/img/noImgIpn.png differ diff --git a/docs/img/noProfile.png b/docs/img/noProfile.png new file mode 100644 index 00000000..4bc460a1 Binary files /dev/null and b/docs/img/noProfile.png differ diff --git a/docs/img/noticias.png b/docs/img/noticias.png new file mode 100644 index 00000000..6372ab0f Binary files /dev/null and b/docs/img/noticias.png differ diff --git a/docs/img/projects.png b/docs/img/projects.png new file mode 100644 index 00000000..5b101b44 Binary files /dev/null and b/docs/img/projects.png differ diff --git a/docs/img/publicaciones.png b/docs/img/publicaciones.png new file mode 100644 index 00000000..88421747 Binary files /dev/null and b/docs/img/publicaciones.png differ diff --git a/docs/img/testvocacional.png b/docs/img/testvocacional.png new file mode 100644 index 00000000..ba623010 Binary files /dev/null and b/docs/img/testvocacional.png differ diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 00000000..2c97e4c1 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,201 @@ + + + + + + + + + + + estuDÍA + +
+
+

+

e s t u D Í A


+

¡Bienvenido/a!


+
+
+


+


+


+

Ingresa con


+


+

¿Ya tienes cuenta? Regístrate

+
+ +
+ +
+
+
+ + +
+ +
+
+
+
+
+
+
+
+
+
+ +
+
+ + +
+ +
+
+
+

Test Vocacional

+
+
+ test +
+
+
+
+

Convocatoria IPN

+
+
+ announcement +
+
+
+
+

Carreras con más demanda

+
+
+ test +
+
+
+
+

Guía de Estudio Oficial

+
+
+ announcement +
+
+
+
+

Oficinas Centrales

+
+
+ +
+
+
+
+ +
+
+
    +
  • + + +
    +
    +

    Test Vocacional

    + test +
    +
    +

    Convocatoria IPN

    + announcement +
    +
    +

    Carreras con más demanda

    + test +
    +
    +

    Guía de Estudio Oficial

    + announcement +
    +
    +

    Oficinas Centrales

    + +
    +
  • +
  • + + + +
  • +
+
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/docs/index.js b/docs/index.js new file mode 100644 index 00000000..0bc4891c --- /dev/null +++ b/docs/index.js @@ -0,0 +1,246 @@ +import { actionGoogle, actionFacebook } from './utils/providers.js'; +import { printCreate } from './utils/createUsers.js'; +import { login } from './utils/loginEmail.js'; +import { actionDelete } from './utils/deletePost.js'; +import { saveFirestore } from './utils/saveInFirestore.js'; +import { openModalEdit } from './utils/modalEdit.js'; +import { signOut } from './utils/exit.js'; +import { addLikes } from './utils/countLikes.js'; +import {openModalComent} from './utils/openComment.js' +import { openModal } from './utils/info.js'; + +let getEmail; +export let getName; +let getImg; +let url; +export let userSigned; +const db = firebase.firestore(); +const firstPa = document.getElementById('firstPage'); +const allSite = document.getElementById('allTheSite'); +const filterName = document.querySelector('#searchName'); +const btnFilter = document.querySelector('#searchButtom1'); +const btnMySite = document.querySelector('#myWall'); +const goToPrincipal = document.querySelector('#allComents'); +const printing = document.querySelector('#addComents'); +const btnMod = document.getElementById('btnInfo'); +export const printCreateUser = document.getElementById('logingUsers'); +export const userAction = document.getElementById('actionUser'); +btnMod.addEventListener('click', () => { + openModal(); +}); + +//* ****************************register users */ + +export const userNew = document.getElementById('newUser'); +userNew.addEventListener('click', () => { + printCreate(printCreateUser, userAction); +}); + +//* *****************Login with providers **************************** +const btnGoogle = document.getElementById('loginGoogle'); +btnGoogle.addEventListener('click', actionGoogle); + +const btnFacebook = document.getElementById('loginFacebook'); +btnFacebook.addEventListener('click', actionFacebook); + +//* **************************Login************************************* +const email = document.getElementById('email'); +const passLogin = document.getElementById('password'); + +const registeredUser = document.getElementById('logInUser'); +registeredUser.addEventListener('click', () => { + login(email.value, passLogin.value); +}); + +function printSite() { + firstPa.style.display = 'none'; + allSite.style.display = 'block'; +} + +//* ***********************Active users******************************** +export function theWatcher() { + firebase.auth().onAuthStateChanged((user) => { + if (user) { + console.log('active user'); + printSite(); + userSigned = firebase.auth().currentUser; + if (userSigned != null) { + userSigned.providerData.forEach((profile) => { + if (profile.displayName === null) { + const showEmail = document.getElementById('sayHi'); + getEmail = profile.email; + const showImage = document.getElementById('imgProfile'); + const photo = '
'; + showEmail.innerHTML = `Hola: ${getEmail}`; + showImage.innerHTML = photo; + } else { + const showName = document.getElementById('sayHi'); + const showImage = document.getElementById('imgProfile'); + getName = profile.displayName; + const getImage = profile.photoURL; + showName.innerHTML = `Hola: ${getName}`; + const photo = `
`; + showImage.innerHTML = photo; + } + }); + } + } else { + console.log('no users logged'); + } + }); +} + +theWatcher(); +//* ***********************Button for actionSingout************************ +const buttonClose = document.getElementById('logOut'); +buttonClose.addEventListener('click', () => { + document.querySelector('#firstPage').style.display = 'block'; + document.querySelector('#allTheSite').style.display = 'none'; + signOut(); +}); +//* ***********************Get info profile******************************** +export function getNameProfile() { + userSigned = firebase.auth().currentUser; + if (userSigned != null) { + userSigned.providerData.forEach((profile) => { + if (profile.displayName === null) { + getName = profile.email; + getImg = 'img/noprofile.png'; + } else { + getName = profile.displayName; + getImg = profile.photoURL; + } + }); + } + return getName; +} +//* ***********************Add image in Firestorage******************************** +const fileInput = document.querySelector('#file'); +fileInput.onchange = (e) => { + console.log(e.target.files); + const file = e.target.files[0]; + firebase + .storage() + .ref('images') + .child(file.name) + .put(file) + .then(snap => snap.ref.getDownloadURL()) + .then((link) => { + url = link; + const img = document.createElement('images'); + img.src = link; + document.body.appendChild(img); + }); +}; +//* ***********************Save data firestore for create post********************* +const shareBtn = document.getElementById('btnShare'); +shareBtn.addEventListener('click', () => { + getNameProfile(); + saveFirestore(getName, getImg, db, url); +}); + +//* ***********************Print cards posts******************************** +export function addNewCard(printing1, doc) { + const posting = document.createElement('div'); + const createTarget = `
+
+ +
${doc.data().name} +

${doc.data().date}

+

${doc.data().comments}

+

+

likes ${doc.data().likes}

+

+ +

`; + posting.innerHTML = createTarget; + printing1.appendChild(posting); + const btn2 = document.querySelectorAll('.btnStyles1'); + btn2.forEach(actionBtn => actionBtn.addEventListener('click', actionDelete)); + const btnlike = document.querySelectorAll('.btnLike'); + btnlike.forEach(actionBtnLikes => actionBtnLikes.addEventListener('click', addLikes)); + const btEdit = document.querySelectorAll('.btnStylesEdit'); + btEdit.forEach(open => open.addEventListener('click', openModalEdit)); +} +function addNewCardNoComents(printing2, doc) { + const posting = document.createElement('div'); + const createTarget = `
+
+ +
${doc.data().name} +

${doc.data().date}

+

${doc.data().comments}

+

+

likes ${doc.data().likes}

+
+ +
`; + + posting.innerHTML = createTarget; + printing2.appendChild(posting); + const btnlike = document.querySelectorAll('.btnLike'); + btnlike.forEach(actionBtnLikes => actionBtnLikes.addEventListener('click', addLikes)); + const btnComent = document.querySelectorAll('.btnComment'); + btnComent.forEach(open => open.addEventListener('click', openModalComent)) +} + +// //* **Print coments in real time** +db.collection('publications') + .orderBy('date', 'desc') + .onSnapshot((querySnapshot) => { + printing.innerHTML = ''; + querySnapshot.forEach((doc) => { + // console.log(doc) + addNewCardNoComents(printing, doc); + }); + }); + +//* **Filter wall all users** +function backToPrincipal() { + db.collection('publications') + .orderBy('date', 'desc') + .onSnapshot((querySnapshot) => { + printing.innerHTML = ''; + querySnapshot.forEach((doc) => { + addNewCardNoComents(printing, doc); + }); + }); +} +goToPrincipal.addEventListener('click', backToPrincipal); + +// ***Filter post of me** +function filterMyComments() { + console.log(filterName.value); + db.collection('publications') + .where('name', '==', getName || getEmail) + .orderBy('date', 'desc') + .onSnapshot((filters) => { + printing.innerHTML = ''; + filters.forEach((doc) => { + addNewCard(printing, doc); + }); + }); +} +btnMySite.addEventListener('click', filterMyComments); + +//* *****************Search By Name **********/ +function filterBySearch() { + db.collection('publications') + .where('name', '==', filterName.value) + .orderBy('date', 'desc') + .onSnapshot((filters) => { + printing.innerHTML = ''; + filters.forEach((doc) => { + addNewCardNoComents(printing, doc); + }); + }); +} +btnFilter.addEventListener('click', filterBySearch); + +// if we also use ".orderBy("Date", "desc")" the real time doesn't work anymore, +// because it needs an index, we did it but it doesn't detect it diff --git a/docs/style.css b/docs/style.css new file mode 100644 index 00000000..22f1d2fa --- /dev/null +++ b/docs/style.css @@ -0,0 +1,455 @@ +/* Initial page */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + width: 100%; +} + +body { + font-family: "Poppins", sans-serif; + background-color: #f5f1f1; +} + +#firstPage { + width: auto; + height: auto; + background: linear-gradient(275.19deg, #c80a48 1.46%, #48020d 89.22%); + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); + border: 10px; + flex-direction: row; + flex-wrap: wrap; + align-items: stretch; + display: block; +} + +#firstElement { + padding: 70px; + margin: 5px; + width: 100%; + flex-basis: auto; + height: auto; + text-align: center; + border-radius: 0px; + font-size: 18px; + color: #ffffff; +} + +.logIn { + background: #c87b7b; + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); + border-radius: 32px; + border-color: #c87b7b; + color: #ffffff; + width: 123px; + height: 34px; + font-size: 18px; +} + +#newUser { + font-size: 14px; + width: 100%; +} + +/*#createUser { + display: none; +}*/ + +#logingUsers { + display: block; +} + +#allTheSite { + display: none; +} + +.boxInput { + background: #ffffff; + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); + border-radius: 32px; + border-color: #ffffff; + width: 260px; + height: 41px; + font-size: 18px; +} + +.text-box { + background-color: #fff; + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); + border-radius: 15px; + font-family: 'Montserrat', sans-serif; + display: flex; + justify-content: space-around; + color:#5e6973; +} + + +.subtitle h3 { + font-family: "Roboto", sans-serif; + color: #040404; + letter-spacing: 2px; + font-weight: 300; + margin: 10px 0px 10px 5px; +} +.subtitle{ + width: 100%; +} + +.hide { + display: none; +} + +.mobile-view { + display: contents; +} + +.desktop-view { + display: none; +} + +.margin-top { + margin-top: 40px; +} + +/*Header*/ +.my-header { + position: sticky !important; + width: 100%; + height: 100%; + left: 0px; + top: 0px; + background: linear-gradient(336.74deg, #c80a48 1.46%, #48020d 85.12%); + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); + border-radius: 0px 0px 50px 0px; +} + +.logo img { + width: 250px; + padding: 10px 20px 10px 15px; +} + +.user { + width: 70px; + border-radius: 80%; + padding: 5px; + margin-left: 4px; + display: flex; + flex-direction: column; + justify-content: space-around; +} + +.flex-box-horizontal { + display: flex; + justify-content: space-between; +} + +#sayHi { + flex-direction: row; +} +.textPost{ + display: flex; + justify-content: flex-start; +border-radius: 0px 35px 0px 35px; +border: 1px solid #5e6973; +width: 200%; +height: auto; +resize: none; + +} +.postStyle{ + width: auto; + height: auto; + +} + +/* Buttons */ +.btnLike { + width: 30px; +} + +.like { + display: flex; + justify-content: flex-end; +} + +.btnStyles, +.btnStyles1, +.btnStylesEdit, +.btnSty, +.btnStylesC, +.btnComment, +.btnAddC { + font-size: 15px; + width: 100px; + height: 25px; + border: 1px solid #8a0829; + border-radius: 0.25rem; + color: rgb(58, 57, 57); + transition: color 0.15s ease-in-out; +} + +.btnStyles:hover, +.btnStyles:active, +.btnStyles:focus { + color: #fff; + background-color: #8a0829; +} + +.btnStyles1:hover, +.btnStyles1:active, +.btnStyles1:focus { + color: #fff; + background-color: #8a0829; +} + +.btnStylesEdit:hover, +.btnStylesEdit:active, +.btnStylesEdit:focus { + color: #fff; + background-color: #8a0829; +} + +/* Publications */ +figure{ + display: flex; + justify-content: space-around; + +} +.imageBox { + height: auto; + border-radius: 100px; + width: 90px; +} + + +.imgProfilePost { + width: 50px; + height: auto; + border-radius: 100px; +} + +.divBtnsPost { + display: flex; + justify-content: space-around; + padding: 20px; +} + +.divName { + display: flex; + justify-content: center; + color: #48020d; + font-family: "Quicksand", sans-serif; +} + +.styleNamePost { + display: flex; + width: 100%; + height: 100%; + opacity: 0.9; + left: 0px; + top: 0px; + border-radius: 5px 5px 5px 5px; + color: #48020d; + font-family: "Quicksand", sans-serif; +} + +.nameDate { + display: flex; + flex-direction: column; +} + +#card2 { + text-align: justify; + margin-bottom: 16px; + margin-top: 16px; + background-color: #ffff; + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); + border-radius: 15px; + padding: 15px 20px; + font-family: "Montserrat",sans-serif; +} + +.viewFormEdit { + background-color: rgba(0, 0, 0, 0.4); + padding: 5%; + border-radius: 5px; + position: fixed; + z-index: 3000; + display: flex; + flex-direction: column; + width: 90%; +} + +/*Tabs*/ +.tabs { + width: 100%; + float: none; + list-style: none; + position: relative; + text-align: left; +} + +.tabs li { + float: left; + display: block; + width: 50%; +} + +.tabs input[type="radio"] { + position: absolute; + top: 0; + left: -9999px; +} + +.tabs label { + display: block; + padding: 14px 21px; + border-radius: 1px 1px 0 0; + font-size: 1em; + font-family: "Roboto",sans-serif; + letter-spacing: 1px; + font-weight: normal; + text-align: center; + background: #fff; + cursor: pointer; + position: relative; + top: 4px; + width: 100%; + -moz-transition: all 0.2s ease-in-out; + -o-transition: all 0.2s ease-in-out; + -webkit-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; +} + +.tabs label:hover { + background: linear-gradient(336.74deg, #c80a48 1.46%, #48020d 85.12%); + color: #fff; +} + +.tabs .tab-content { + z-index: 2; + display: none; + overflow: hidden; + width: 100%; + font-size: 17px; + line-height: 25px; + padding: 25px; + position: absolute; + top: 53px; + left: 0; + background: #fce7e7; + border-radius: 10px; +} + +.tabs [id^="tab"]:checked + label { + top: 0; + padding-top: 17px; + background: linear-gradient(336.74deg, #c80a48 1.46%, #48020d 85.12%); + color: #fff; +} + +.tabs [id^="tab"]:checked ~ [id^="tab-content"] { + display: block; +} + +/*#text-box { + border-radius: 8px 8px 8px 8px; + width: 100%; + height: 95%; + resize: none; +}*/ + +/*Card modal*/ +.card-modal { + display: none; + position: fixed; + z-index: 100; + left: 0; + top: 0; + width: 100%; /* Full width */ + height: 100%; /* Full height */ + overflow: auto; /* Enable scroll if needed */ + background-color: rgb(0, 0, 0); /* Fallback color */ + background-color: rgba(0, 0, 0, 0.4); /* Black w/ opacity */ +} + +.modal{ + font-family: "Roboto",sans-serif; + color: #040404; + font-size: 18px; + text-align: center; + background-color: #fff; + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); + padding: 30px; + position: fixed; + z-index: 3000; + overflow: auto; + min-height: 250px; + border-radius: 0px 35px 0px 35px; + border: 2px solid #8a0829; + +} + + +/*Media querys*/ +@media screen and (min-width: 450px) { + .my-header { + border-radius: 0px; + } + + .modal { + width: 70%; /* Could be more or less, depending on screen size */ + margin: 30% auto; /* 15% from the top and centered */ + } +} + +@media screen and (min-width: 500px) { + .col-8 { + width: 60%; + padding: 0px 15px; + } + .col-6 { + width: 50%; + padding: 0px 15px 0px 0px; + } + + .col-4 { + width: 40%; + padding: 0px 15px; + } + + .text-box { + display: flex; + justify-content: flex-start; + } + + .img { + width: 100%; + height: auto; + } + + .col-4, + .col-8 { + float: right; + } + .card-img-bottom { + width: 100%; + height: auto; + } + .desktop-view { + display: contents; + } + + ul .mobile-view { + display: none; + width: 100%; + } + .tabs li { + width: 100%; + } + + .modal { + width: 35%; /* Could be more or less, depending on screen size */ + margin: 15% auto; /* 15% from the top and centered */ + } +} diff --git a/docs/utils/countLikes.js b/docs/utils/countLikes.js new file mode 100644 index 00000000..c711b036 --- /dev/null +++ b/docs/utils/countLikes.js @@ -0,0 +1,39 @@ +import { userSigned } from '../index.js'; +export let addLikes = (e) => { + let idPost; + idPost = e.target.name; + const docRef = firebase.firestore().collection('publications').doc(idPost); + docRef.get().then((doc) => { + if (doc.exists) { + const arrayLikes = doc.data(); + console.log(arrayLikes); + + if (arrayLikes.addLikes.includes(userSigned.uid)) { + console.log('you already add a like'); + firebase + .firestore() + .collection('publications') + .doc(idPost) + .update({ + likes: firebase.firestore.FieldValue.increment(-1), + addLikes: firebase.firestore.FieldValue.arrayRemove(userSigned.uid), + }); + } else { + console.log('this is your first like'); + firebase + .firestore() + .collection('publications') + .doc(idPost) + .update({ + likes: firebase.firestore.FieldValue.increment(+1), + addLikes: firebase.firestore.FieldValue.arrayUnion(userSigned.uid), + }); + } + } else { + // doc.data() will be undefined in this case + console.log('No such document!'); + } + }).catch((error) => { + console.log('Error getting document:', error); + }); +}; diff --git a/docs/utils/createUsers.js b/docs/utils/createUsers.js new file mode 100644 index 00000000..c775f59d --- /dev/null +++ b/docs/utils/createUsers.js @@ -0,0 +1,35 @@ +/* eslint no-param-reassign: "error" */ +function createUser(emailNew, password) { + firebase + .auth() + .createUserWithEmailAndPassword(emailNew, password) + .catch((error) => { + const errorCode = error.code; + const errorMessage = error.message; + switch (errorCode) { + case 'auth/invalid-email': + alert(`Correo lisiado ${errorMessage}`); + break; + case 'auth/weak-password': + alert(`por tu seguridad usa una contraseña más larga ${errorMessage}`); + break; + default: + alert('ingreso lisiado'); + } + }); +} +export function printCreate(printCreateUser1, userAction2) { + printCreateUser1.style.display = 'none'; + const register = `
+


+


+

+
`; + userAction2.innerHTML = register; + const emailNew = document.getElementById('emailNw'); + const password = document.getElementById('passwordNw'); + const createNewUser = document.getElementById('createUserNw'); + createNewUser.addEventListener('click', () => { + createUser(emailNew.value, password.value); + }); +} diff --git a/docs/utils/deletePost.js b/docs/utils/deletePost.js new file mode 100644 index 00000000..f2587b07 --- /dev/null +++ b/docs/utils/deletePost.js @@ -0,0 +1,10 @@ +//* *******************delete comments**************************************** +export const actionDelete = (e) => { + firebase.firestore().collection('publications').doc(e.target.id).delete() + .then(() => { + console.log('Document successfully deleted!'); + }) + .catch((error) => { + console.error('Error removing document: ', error); + }); +}; diff --git a/docs/utils/exit.js b/docs/utils/exit.js new file mode 100644 index 00000000..47f2dea9 --- /dev/null +++ b/docs/utils/exit.js @@ -0,0 +1,10 @@ +//* *******************user singout***************************** +export function signOut() { + firebase.auth().signOut() + .then(() => { + console.log('Out'); + }) + .catch((error) => { + console.log(error); + }); +} diff --git a/docs/utils/info.js b/docs/utils/info.js new file mode 100644 index 00000000..1fab32c9 --- /dev/null +++ b/docs/utils/info.js @@ -0,0 +1,19 @@ +export function openModal() { + const divInfoModal = document.createElement('div'); + divInfoModal.className = 'modal'; + divInfoModal.id = 'modalInfo'; + document.body.appendChild(divInfoModal); + const textModal = 'Creamos esta comunidad para que compartas' + + ' y encuentres algo que te pueda ayudar en tu examén de admisión. ' + + 'No estudies solo, juntos podemos lograrlo...' + + '\n ~La técnica al servicio de la Patria~ \n \n'; + const btClose = document.createElement('button'); + btClose.className = 'btnStyles'; + btClose.id = 'btnCloseM'; + btClose.innerHTML = 'Cerrar'; + divInfoModal.innerText = textModal; + divInfoModal.appendChild(btClose); + btClose.addEventListener('click', () => { + document.body.removeChild(divInfoModal); + }); +} diff --git a/docs/utils/loginEmail.js b/docs/utils/loginEmail.js new file mode 100644 index 00000000..cd2f9496 --- /dev/null +++ b/docs/utils/loginEmail.js @@ -0,0 +1,22 @@ +//* **************login register users******************************** +export function login(email, passLogin) { + if (!email || !passLogin) return 'esta mal lisiada'; + if (passLogin.length < 6) return 'no cumple lisiada'; + return firebase.auth().signInWithEmailAndPassword(email, passLogin) + .catch((error) => { + const errorCode = error.code; + const errorMessage = error.message; + switch (errorCode) { + case 'auth/invalid-email': + alert(`Correo electrónico incorrecto ${errorMessage}`); + console.log(errorCode); + break; + case 'auth/wrong-password': + alert(`Verifica tu contraseña ${errorMessage}`); + console.log(errorCode); + break; + default: + alert('sigue participando '); + } + }); +} diff --git a/docs/utils/modalEdit.js b/docs/utils/modalEdit.js new file mode 100644 index 00000000..ec9093b8 --- /dev/null +++ b/docs/utils/modalEdit.js @@ -0,0 +1,39 @@ +let divFormUpdate; +const actionEdit = (e) => { + console.log(e.target.value); + const comment = document.getElementById('newComment').value; + console.log(comment); + return firebase.firestore().collection('publications').doc(e.target.value).update({ + comments: comment, + }) + .then(() => { + console.log('Document successfully written!'); + document.body.removeChild(divFormUpdate); + }) + .catch((error) => { + console.error('Error writing document: ', error); + }); +}; + +const closed = () => { + document.body.removeChild(divFormUpdate); +}; +export function openModalEdit(e) { + const id = e.target.getAttribute('data-doc'); + divFormUpdate = document.createElement('div'); + divFormUpdate.className = 'viewFormEdit'; + divFormUpdate.id = 'formUpdate'; + document.body.appendChild(divFormUpdate); + firebase.firestore().collection('publications').doc(id).get() + .then((consultDb) => { + const doc = consultDb.data(); + const divUpdate = ` +

+

`; + divFormUpdate.innerHTML = divUpdate; + const btnUpdate = document.querySelectorAll('.btnSty'); + btnUpdate.forEach(update => update.addEventListener('click', actionEdit)); + const btnClose = document.querySelectorAll('.btnStylesC'); + btnClose.forEach(close => close.addEventListener('click', closed)); + }); +} diff --git a/docs/utils/openComment.js b/docs/utils/openComment.js new file mode 100644 index 00000000..a00dc70e --- /dev/null +++ b/docs/utils/openComment.js @@ -0,0 +1,52 @@ +import { getName,getNameProfile } from '../index.js'; +let idAddComent; +let idAddComentDiv; +export function openModalComent(e) { + idAddComent = e.target.getAttribute('data-id'); + idAddComentDiv = e.target.getAttribute('title');; + console.log(idAddComent); + let divFormUpdate = document.createElement('div'); + divFormUpdate.className = 'viewFormEdit'; + divFormUpdate.id = 'formUpdate'; + document.body.appendChild(divFormUpdate); + firebase.firestore().collection('publications').doc(idAddComent).get() + .then((consultDb) => { + const doc = consultDb.data(); + const divUpdate = ` +

+

`; + divFormUpdate.innerHTML = divUpdate; + const btnAddC= document.querySelectorAll('.btnAddC'); + btnAddC.forEach(update => update.addEventListener('click', actionComent)); + const btnClose = document.querySelectorAll('.btnStylesC'); + btnClose.forEach(close => close.addEventListener('click', closedModal)); + + }); + const closedModal = () => { + document.body.removeChild(divFormUpdate); + }; + } + +const actionComent = (e) => { + console.log(getName); + let nwC= document.getElementById('newComment').value; + console.log(nwC); + firebase.firestore().collection('coments').add({ + opinion: nwC, + idC: e.target.value + }) + .then((docRef) => { + console.log('Document written with ID: ', docRef.id); + + firebase + .firestore() + .collection('publications') + .doc(idAddComent) + .update({ + addComent: firebase.firestore.FieldValue.arrayUnion(" "+ getNameProfile()+": " + nwC) + }); + }) + .catch((error) => { + console.error('Error adding document: ', error); + }); + } diff --git a/docs/utils/providers.js b/docs/utils/providers.js new file mode 100644 index 00000000..17609351 --- /dev/null +++ b/docs/utils/providers.js @@ -0,0 +1,10 @@ +//* ****************login with socialmedia************************** +export function actionGoogle() { + const providerGoogle = new firebase.auth.GoogleAuthProvider(); + return firebase.auth().signInWithPopup(providerGoogle); +} + +export function actionFacebook() { + const providerFacebook = new firebase.auth.FacebookAuthProvider(); + return firebase.auth().signInWithPopup(providerFacebook); +} diff --git a/docs/utils/saveInFirestore.js b/docs/utils/saveInFirestore.js new file mode 100644 index 00000000..4048b1a9 --- /dev/null +++ b/docs/utils/saveInFirestore.js @@ -0,0 +1,28 @@ +export function saveFirestore(name, img, data, link) { + let date = new Date(); + date += Date.now(); + const date1 = date.slice(0, 25); + const coment = document.querySelector('#text-box'); + console.log(`post ${coment.value}`); + //* ******************add comments in firestore********************************* + data.collection('publications').add({ + name, + photo: img, + date: date1, + comments: coment.value, + likes: '', + image: link || '', + addLikes: '', + addComent:'', + }) + .then((docRef) => { + console.log('Document written with ID: ', docRef.id); + console.log(coment.value); + const cleanBox = document.getElementById('text-box'); + cleanBox.value = ''; + console.log(cleanBox); + }) + .catch((error) => { + console.error('Error adding document: ', error); + }); +} diff --git a/firebase.json b/firebase.json new file mode 100644 index 00000000..458fa64e --- /dev/null +++ b/firebase.json @@ -0,0 +1,41 @@ +{ + "database": { + "rules": "database.rules.json" + }, + "functions": { + "predeploy": [ + "npm --prefix \"$RESOURCE_DIR\" run lint" + ] + }, + "hosting": { + "public": "public", + "ignore": [ + "firebase.json", + "**/.*", + "**/node_modules/**" + ], + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ] + }, + "storage": { + "rules": "storage.rules" + }, + "emulators": { + "functions": { + "port": 5001 + }, + "database": { + "port": 9000 + }, + "hosting": { + "port": 5000 + }, + "pubsub": { + "port": 8085 + } + } +} diff --git a/firestore.rules b/firestore.rules new file mode 100644 index 00000000..31eda174 --- /dev/null +++ b/firestore.rules @@ -0,0 +1,7 @@ +service cloud.firestore { + match /databases/{database}/documents { + match /{document=**} { + allow read, write; + } + } +} diff --git a/functions/.eslintrc.json b/functions/.eslintrc.json new file mode 100644 index 00000000..6b6beb65 --- /dev/null +++ b/functions/.eslintrc.json @@ -0,0 +1,123 @@ +{ + "parserOptions": { + // Required for certain syntax usages + "ecmaVersion": 2017 + }, + "plugins": [ + "promise" + ], + "extends": "eslint:recommended", + "rules": { + // Removed rule "disallow the use of console" from recommended eslint rules + "no-console": "off", + + // Removed rule "disallow multiple spaces in regular expressions" from recommended eslint rules + "no-regex-spaces": "off", + + // Removed rule "disallow the use of debugger" from recommended eslint rules + "no-debugger": "off", + + // Removed rule "disallow unused variables" from recommended eslint rules + "no-unused-vars": "off", + + // Removed rule "disallow mixed spaces and tabs for indentation" from recommended eslint rules + "no-mixed-spaces-and-tabs": "off", + + // Removed rule "disallow the use of undeclared variables unless mentioned in /*global */ comments" from recommended eslint rules + "no-undef": "off", + + // Warn against template literal placeholder syntax in regular strings + "no-template-curly-in-string": 1, + + // Warn if return statements do not either always or never specify values + "consistent-return": 1, + + // Warn if no return statements in callbacks of array methods + "array-callback-return": 1, + + // Require the use of === and !== + "eqeqeq": 2, + + // Disallow the use of alert, confirm, and prompt + "no-alert": 2, + + // Disallow the use of arguments.caller or arguments.callee + "no-caller": 2, + + // Disallow null comparisons without type-checking operators + "no-eq-null": 2, + + // Disallow the use of eval() + "no-eval": 2, + + // Warn against extending native types + "no-extend-native": 1, + + // Warn against unnecessary calls to .bind() + "no-extra-bind": 1, + + // Warn against unnecessary labels + "no-extra-label": 1, + + // Disallow leading or trailing decimal points in numeric literals + "no-floating-decimal": 2, + + // Warn against shorthand type conversions + "no-implicit-coercion": 1, + + // Warn against function declarations and expressions inside loop statements + "no-loop-func": 1, + + // Disallow new operators with the Function object + "no-new-func": 2, + + // Warn against new operators with the String, Number, and Boolean objects + "no-new-wrappers": 1, + + // Disallow throwing literals as exceptions + "no-throw-literal": 2, + + // Require using Error objects as Promise rejection reasons + "prefer-promise-reject-errors": 2, + + // Enforce “for” loop update clause moving the counter in the right direction + "for-direction": 2, + + // Enforce return statements in getters + "getter-return": 2, + + // Disallow await inside of loops + "no-await-in-loop": 2, + + // Disallow comparing against -0 + "no-compare-neg-zero": 2, + + // Warn against catch clause parameters from shadowing variables in the outer scope + "no-catch-shadow": 1, + + // Disallow identifiers from shadowing restricted names + "no-shadow-restricted-names": 2, + + // Enforce return statements in callbacks of array methods + "callback-return": 2, + + // Require error handling in callbacks + "handle-callback-err": 2, + + // Warn against string concatenation with __dirname and __filename + "no-path-concat": 1, + + // Prefer using arrow functions for callbacks + "prefer-arrow-callback": 1, + + // Return inside each then() to create readable and reusable Promise chains. + // Forces developers to return console logs and http calls in promises. + "promise/always-return": 2, + + //Enforces the use of catch() on un-returned promises + "promise/catch-or-return": 2, + + // Warn against nested then() or catch() statements + "promise/no-nesting": 1 + } +} diff --git a/functions/.gitignore b/functions/.gitignore new file mode 100644 index 00000000..40b878db --- /dev/null +++ b/functions/.gitignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/functions/index.js b/functions/index.js new file mode 100644 index 00000000..bd698a23 --- /dev/null +++ b/functions/index.js @@ -0,0 +1,8 @@ +const functions = require('firebase-functions'); + +// // Create and Deploy Your First Cloud Functions +// // https://firebase.google.com/docs/functions/write-firebase-functions +// +// exports.helloWorld = functions.https.onRequest((request, response) => { +// response.send("Hello from Firebase!"); +// }); diff --git a/functions/package.json b/functions/package.json new file mode 100644 index 00000000..7b569841 --- /dev/null +++ b/functions/package.json @@ -0,0 +1,25 @@ +{ + "name": "functions", + "description": "Cloud Functions for Firebase", + "scripts": { + "lint": "eslint .", + "serve": "firebase emulators:start --only functions", + "shell": "firebase functions:shell", + "start": "npm run shell", + "deploy": "firebase deploy --only functions", + "logs": "firebase functions:log" + }, + "engines": { + "node": "8" + }, + "dependencies": { + "firebase-admin": "^8.9.0", + "firebase-functions": "^3.3.0" + }, + "devDependencies": { + "eslint": "^5.12.0", + "eslint-plugin-promise": "^4.0.1", + "firebase-functions-test": "^0.1.6" + }, + "private": true +} diff --git a/package.json b/package.json index 9a365577..2aa2af5b 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "@babel/plugin-transform-modules-commonjs": "^7.2.0", "babel-core": "^7.0.0-bridge.0", "babel-jest": "^24.8.0", - "eslint": "^6.0.1", + "eslint": "^6.8.0", "eslint-config-airbnb-base": "^13.2.0", "eslint-plugin-import": "^2.18.2", "eslint-plugin-jest": "^22.1.2", @@ -32,5 +32,10 @@ "serve": "^11.0.2", "stylelint": "^10.1.0", "stylelint-config-recommended": "^2.2.0" + }, + "dependencies": { + "firebase": "^7.13.2", + "firebase-admin": "^8.10.0", + "mock-cloud-firestore": "^0.12.0" } -} \ No newline at end of file +} diff --git a/public/index.html b/public/index.html new file mode 100644 index 00000000..cf56d193 --- /dev/null +++ b/public/index.html @@ -0,0 +1,65 @@ + + + + + + Welcome to Firebase Hosting + + + + + + + + + + + + + + +
+

Welcome

+

Firebase Hosting Setup Complete

+

You're seeing this because you've successfully setup Firebase Hosting. Now it's time to go build something extraordinary!

+ Open Hosting Documentation +
+

Firebase SDK Loading…

+ + + + diff --git a/src/.eslintrc.js b/src/.eslintrc.js new file mode 100644 index 00000000..5f656948 --- /dev/null +++ b/src/.eslintrc.js @@ -0,0 +1,17 @@ +module.exports = { + "env": { + "es6": true, + "node": true + }, + "extends": "eslint:recommended", + "globals": { + "Atomics": "readonly", + "SharedArrayBuffer": "readonly" + }, + "parserOptions": { + "ecmaVersion": 2018, + "sourceType": "module" + }, + "rules": { + } +}; \ No newline at end of file diff --git a/src/example.js b/src/example.js deleted file mode 100644 index 52d2c7ea..00000000 --- a/src/example.js +++ /dev/null @@ -1,3 +0,0 @@ -export const example = () => { - // aquí tu código -} \ No newline at end of file diff --git a/src/img/Group.svg b/src/img/Group.svg new file mode 100644 index 00000000..28feeabc --- /dev/null +++ b/src/img/Group.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/img/LogoIPN.png b/src/img/LogoIPN.png new file mode 100644 index 00000000..a064b50e Binary files /dev/null and b/src/img/LogoIPN.png differ diff --git a/src/img/convocatoria.png b/src/img/convocatoria.png new file mode 100644 index 00000000..ae47164d Binary files /dev/null and b/src/img/convocatoria.png differ diff --git a/src/img/demanda.png b/src/img/demanda.png new file mode 100644 index 00000000..059401f3 Binary files /dev/null and b/src/img/demanda.png differ diff --git a/src/img/estudia.png b/src/img/estudia.png new file mode 100644 index 00000000..ad257d92 Binary files /dev/null and b/src/img/estudia.png differ diff --git a/src/img/facebook.svg b/src/img/facebook.svg new file mode 100644 index 00000000..12ecbe1d --- /dev/null +++ b/src/img/facebook.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/img/gmail.svg b/src/img/gmail.svg new file mode 100644 index 00000000..f99ed6eb --- /dev/null +++ b/src/img/gmail.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/img/guia.png b/src/img/guia.png new file mode 100644 index 00000000..cff145f4 Binary files /dev/null and b/src/img/guia.png differ diff --git a/src/img/img.svg b/src/img/img.svg new file mode 100644 index 00000000..790de8c1 --- /dev/null +++ b/src/img/img.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/img/info.svg b/src/img/info.svg new file mode 100644 index 00000000..3a5019c6 --- /dev/null +++ b/src/img/info.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/img/inicioSesion.png b/src/img/inicioSesion.png new file mode 100644 index 00000000..9fda8d9a Binary files /dev/null and b/src/img/inicioSesion.png differ diff --git a/src/img/init.jpg b/src/img/init.jpg new file mode 100644 index 00000000..b9b0c5fd Binary files /dev/null and b/src/img/init.jpg differ diff --git a/src/img/init0.jpg b/src/img/init0.jpg new file mode 100644 index 00000000..e718d930 Binary files /dev/null and b/src/img/init0.jpg differ diff --git a/src/img/like.svg b/src/img/like.svg new file mode 100644 index 00000000..8bc4a3cf --- /dev/null +++ b/src/img/like.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/img/logo-ipn-blanco.png b/src/img/logo-ipn-blanco.png new file mode 100644 index 00000000..8ec3d5c6 Binary files /dev/null and b/src/img/logo-ipn-blanco.png differ diff --git a/src/img/modal.png b/src/img/modal.png new file mode 100644 index 00000000..aa35564f Binary files /dev/null and b/src/img/modal.png differ diff --git a/src/img/noImgIpn.png b/src/img/noImgIpn.png new file mode 100644 index 00000000..f5ff1f99 Binary files /dev/null and b/src/img/noImgIpn.png differ diff --git a/src/img/noProfile.png b/src/img/noProfile.png new file mode 100644 index 00000000..4bc460a1 Binary files /dev/null and b/src/img/noProfile.png differ diff --git a/src/img/noticias.png b/src/img/noticias.png new file mode 100644 index 00000000..6372ab0f Binary files /dev/null and b/src/img/noticias.png differ diff --git a/src/img/projects.png b/src/img/projects.png new file mode 100644 index 00000000..5b101b44 Binary files /dev/null and b/src/img/projects.png differ diff --git a/src/img/publicaciones.png b/src/img/publicaciones.png new file mode 100644 index 00000000..88421747 Binary files /dev/null and b/src/img/publicaciones.png differ diff --git a/src/img/testvocacional.png b/src/img/testvocacional.png new file mode 100644 index 00000000..ba623010 Binary files /dev/null and b/src/img/testvocacional.png differ diff --git a/src/index.html b/src/index.html index bb6937cc..2c97e4c1 100644 --- a/src/index.html +++ b/src/index.html @@ -1,12 +1,201 @@ + - Document + + + + estuDÍA - - +
+
+

+

e s t u D Í A


+

¡Bienvenido/a!


+
+
+


+


+


+

Ingresa con


+


+

¿Ya tienes cuenta? Regístrate

+
+ +
+ +
+
+
+ + +
+ +
+
+
+
+
+
+
+
+
+
+ +
+
+ + +
+ +
+
+
+

Test Vocacional

+
+
+ test +
+
+
+
+

Convocatoria IPN

+
+
+ announcement +
+
+
+
+

Carreras con más demanda

+
+
+ test +
+
+
+
+

Guía de Estudio Oficial

+
+
+ announcement +
+
+
+
+

Oficinas Centrales

+
+
+ +
+
+
+
+ +
+
+
    +
  • + + +
    +
    +

    Test Vocacional

    + test +
    +
    +

    Convocatoria IPN

    + announcement +
    +
    +

    Carreras con más demanda

    + test +
    +
    +

    Guía de Estudio Oficial

    + announcement +
    +
    +

    Oficinas Centrales

    + +
    +
  • +
  • + + + +
  • +
+
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/src/index.js b/src/index.js index 35868bab..0bc4891c 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,246 @@ -import { example } from './example.js'; +import { actionGoogle, actionFacebook } from './utils/providers.js'; +import { printCreate } from './utils/createUsers.js'; +import { login } from './utils/loginEmail.js'; +import { actionDelete } from './utils/deletePost.js'; +import { saveFirestore } from './utils/saveInFirestore.js'; +import { openModalEdit } from './utils/modalEdit.js'; +import { signOut } from './utils/exit.js'; +import { addLikes } from './utils/countLikes.js'; +import {openModalComent} from './utils/openComment.js' +import { openModal } from './utils/info.js'; -example(); +let getEmail; +export let getName; +let getImg; +let url; +export let userSigned; +const db = firebase.firestore(); +const firstPa = document.getElementById('firstPage'); +const allSite = document.getElementById('allTheSite'); +const filterName = document.querySelector('#searchName'); +const btnFilter = document.querySelector('#searchButtom1'); +const btnMySite = document.querySelector('#myWall'); +const goToPrincipal = document.querySelector('#allComents'); +const printing = document.querySelector('#addComents'); +const btnMod = document.getElementById('btnInfo'); +export const printCreateUser = document.getElementById('logingUsers'); +export const userAction = document.getElementById('actionUser'); +btnMod.addEventListener('click', () => { + openModal(); +}); + +//* ****************************register users */ + +export const userNew = document.getElementById('newUser'); +userNew.addEventListener('click', () => { + printCreate(printCreateUser, userAction); +}); + +//* *****************Login with providers **************************** +const btnGoogle = document.getElementById('loginGoogle'); +btnGoogle.addEventListener('click', actionGoogle); + +const btnFacebook = document.getElementById('loginFacebook'); +btnFacebook.addEventListener('click', actionFacebook); + +//* **************************Login************************************* +const email = document.getElementById('email'); +const passLogin = document.getElementById('password'); + +const registeredUser = document.getElementById('logInUser'); +registeredUser.addEventListener('click', () => { + login(email.value, passLogin.value); +}); + +function printSite() { + firstPa.style.display = 'none'; + allSite.style.display = 'block'; +} + +//* ***********************Active users******************************** +export function theWatcher() { + firebase.auth().onAuthStateChanged((user) => { + if (user) { + console.log('active user'); + printSite(); + userSigned = firebase.auth().currentUser; + if (userSigned != null) { + userSigned.providerData.forEach((profile) => { + if (profile.displayName === null) { + const showEmail = document.getElementById('sayHi'); + getEmail = profile.email; + const showImage = document.getElementById('imgProfile'); + const photo = '
'; + showEmail.innerHTML = `Hola: ${getEmail}`; + showImage.innerHTML = photo; + } else { + const showName = document.getElementById('sayHi'); + const showImage = document.getElementById('imgProfile'); + getName = profile.displayName; + const getImage = profile.photoURL; + showName.innerHTML = `Hola: ${getName}`; + const photo = `
`; + showImage.innerHTML = photo; + } + }); + } + } else { + console.log('no users logged'); + } + }); +} + +theWatcher(); +//* ***********************Button for actionSingout************************ +const buttonClose = document.getElementById('logOut'); +buttonClose.addEventListener('click', () => { + document.querySelector('#firstPage').style.display = 'block'; + document.querySelector('#allTheSite').style.display = 'none'; + signOut(); +}); +//* ***********************Get info profile******************************** +export function getNameProfile() { + userSigned = firebase.auth().currentUser; + if (userSigned != null) { + userSigned.providerData.forEach((profile) => { + if (profile.displayName === null) { + getName = profile.email; + getImg = 'img/noprofile.png'; + } else { + getName = profile.displayName; + getImg = profile.photoURL; + } + }); + } + return getName; +} +//* ***********************Add image in Firestorage******************************** +const fileInput = document.querySelector('#file'); +fileInput.onchange = (e) => { + console.log(e.target.files); + const file = e.target.files[0]; + firebase + .storage() + .ref('images') + .child(file.name) + .put(file) + .then(snap => snap.ref.getDownloadURL()) + .then((link) => { + url = link; + const img = document.createElement('images'); + img.src = link; + document.body.appendChild(img); + }); +}; +//* ***********************Save data firestore for create post********************* +const shareBtn = document.getElementById('btnShare'); +shareBtn.addEventListener('click', () => { + getNameProfile(); + saveFirestore(getName, getImg, db, url); +}); + +//* ***********************Print cards posts******************************** +export function addNewCard(printing1, doc) { + const posting = document.createElement('div'); + const createTarget = `
+
+ +
${doc.data().name} +

${doc.data().date}

+

${doc.data().comments}

+

+

likes ${doc.data().likes}

+

+ +

`; + posting.innerHTML = createTarget; + printing1.appendChild(posting); + const btn2 = document.querySelectorAll('.btnStyles1'); + btn2.forEach(actionBtn => actionBtn.addEventListener('click', actionDelete)); + const btnlike = document.querySelectorAll('.btnLike'); + btnlike.forEach(actionBtnLikes => actionBtnLikes.addEventListener('click', addLikes)); + const btEdit = document.querySelectorAll('.btnStylesEdit'); + btEdit.forEach(open => open.addEventListener('click', openModalEdit)); +} +function addNewCardNoComents(printing2, doc) { + const posting = document.createElement('div'); + const createTarget = `
+
+ +
${doc.data().name} +

${doc.data().date}

+

${doc.data().comments}

+

+

likes ${doc.data().likes}

+ + +
    Comentarios:
    +
    ${doc.data().addComent}
`; + + posting.innerHTML = createTarget; + printing2.appendChild(posting); + const btnlike = document.querySelectorAll('.btnLike'); + btnlike.forEach(actionBtnLikes => actionBtnLikes.addEventListener('click', addLikes)); + const btnComent = document.querySelectorAll('.btnComment'); + btnComent.forEach(open => open.addEventListener('click', openModalComent)) +} + +// //* **Print coments in real time** +db.collection('publications') + .orderBy('date', 'desc') + .onSnapshot((querySnapshot) => { + printing.innerHTML = ''; + querySnapshot.forEach((doc) => { + // console.log(doc) + addNewCardNoComents(printing, doc); + }); + }); + +//* **Filter wall all users** +function backToPrincipal() { + db.collection('publications') + .orderBy('date', 'desc') + .onSnapshot((querySnapshot) => { + printing.innerHTML = ''; + querySnapshot.forEach((doc) => { + addNewCardNoComents(printing, doc); + }); + }); +} +goToPrincipal.addEventListener('click', backToPrincipal); + +// ***Filter post of me** +function filterMyComments() { + console.log(filterName.value); + db.collection('publications') + .where('name', '==', getName || getEmail) + .orderBy('date', 'desc') + .onSnapshot((filters) => { + printing.innerHTML = ''; + filters.forEach((doc) => { + addNewCard(printing, doc); + }); + }); +} +btnMySite.addEventListener('click', filterMyComments); + +//* *****************Search By Name **********/ +function filterBySearch() { + db.collection('publications') + .where('name', '==', filterName.value) + .orderBy('date', 'desc') + .onSnapshot((filters) => { + printing.innerHTML = ''; + filters.forEach((doc) => { + addNewCardNoComents(printing, doc); + }); + }); +} +btnFilter.addEventListener('click', filterBySearch); + +// if we also use ".orderBy("Date", "desc")" the real time doesn't work anymore, +// because it needs an index, we did it but it doesn't detect it diff --git a/src/style.css b/src/style.css new file mode 100644 index 00000000..22f1d2fa --- /dev/null +++ b/src/style.css @@ -0,0 +1,455 @@ +/* Initial page */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + width: 100%; +} + +body { + font-family: "Poppins", sans-serif; + background-color: #f5f1f1; +} + +#firstPage { + width: auto; + height: auto; + background: linear-gradient(275.19deg, #c80a48 1.46%, #48020d 89.22%); + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); + border: 10px; + flex-direction: row; + flex-wrap: wrap; + align-items: stretch; + display: block; +} + +#firstElement { + padding: 70px; + margin: 5px; + width: 100%; + flex-basis: auto; + height: auto; + text-align: center; + border-radius: 0px; + font-size: 18px; + color: #ffffff; +} + +.logIn { + background: #c87b7b; + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); + border-radius: 32px; + border-color: #c87b7b; + color: #ffffff; + width: 123px; + height: 34px; + font-size: 18px; +} + +#newUser { + font-size: 14px; + width: 100%; +} + +/*#createUser { + display: none; +}*/ + +#logingUsers { + display: block; +} + +#allTheSite { + display: none; +} + +.boxInput { + background: #ffffff; + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); + border-radius: 32px; + border-color: #ffffff; + width: 260px; + height: 41px; + font-size: 18px; +} + +.text-box { + background-color: #fff; + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); + border-radius: 15px; + font-family: 'Montserrat', sans-serif; + display: flex; + justify-content: space-around; + color:#5e6973; +} + + +.subtitle h3 { + font-family: "Roboto", sans-serif; + color: #040404; + letter-spacing: 2px; + font-weight: 300; + margin: 10px 0px 10px 5px; +} +.subtitle{ + width: 100%; +} + +.hide { + display: none; +} + +.mobile-view { + display: contents; +} + +.desktop-view { + display: none; +} + +.margin-top { + margin-top: 40px; +} + +/*Header*/ +.my-header { + position: sticky !important; + width: 100%; + height: 100%; + left: 0px; + top: 0px; + background: linear-gradient(336.74deg, #c80a48 1.46%, #48020d 85.12%); + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); + border-radius: 0px 0px 50px 0px; +} + +.logo img { + width: 250px; + padding: 10px 20px 10px 15px; +} + +.user { + width: 70px; + border-radius: 80%; + padding: 5px; + margin-left: 4px; + display: flex; + flex-direction: column; + justify-content: space-around; +} + +.flex-box-horizontal { + display: flex; + justify-content: space-between; +} + +#sayHi { + flex-direction: row; +} +.textPost{ + display: flex; + justify-content: flex-start; +border-radius: 0px 35px 0px 35px; +border: 1px solid #5e6973; +width: 200%; +height: auto; +resize: none; + +} +.postStyle{ + width: auto; + height: auto; + +} + +/* Buttons */ +.btnLike { + width: 30px; +} + +.like { + display: flex; + justify-content: flex-end; +} + +.btnStyles, +.btnStyles1, +.btnStylesEdit, +.btnSty, +.btnStylesC, +.btnComment, +.btnAddC { + font-size: 15px; + width: 100px; + height: 25px; + border: 1px solid #8a0829; + border-radius: 0.25rem; + color: rgb(58, 57, 57); + transition: color 0.15s ease-in-out; +} + +.btnStyles:hover, +.btnStyles:active, +.btnStyles:focus { + color: #fff; + background-color: #8a0829; +} + +.btnStyles1:hover, +.btnStyles1:active, +.btnStyles1:focus { + color: #fff; + background-color: #8a0829; +} + +.btnStylesEdit:hover, +.btnStylesEdit:active, +.btnStylesEdit:focus { + color: #fff; + background-color: #8a0829; +} + +/* Publications */ +figure{ + display: flex; + justify-content: space-around; + +} +.imageBox { + height: auto; + border-radius: 100px; + width: 90px; +} + + +.imgProfilePost { + width: 50px; + height: auto; + border-radius: 100px; +} + +.divBtnsPost { + display: flex; + justify-content: space-around; + padding: 20px; +} + +.divName { + display: flex; + justify-content: center; + color: #48020d; + font-family: "Quicksand", sans-serif; +} + +.styleNamePost { + display: flex; + width: 100%; + height: 100%; + opacity: 0.9; + left: 0px; + top: 0px; + border-radius: 5px 5px 5px 5px; + color: #48020d; + font-family: "Quicksand", sans-serif; +} + +.nameDate { + display: flex; + flex-direction: column; +} + +#card2 { + text-align: justify; + margin-bottom: 16px; + margin-top: 16px; + background-color: #ffff; + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); + border-radius: 15px; + padding: 15px 20px; + font-family: "Montserrat",sans-serif; +} + +.viewFormEdit { + background-color: rgba(0, 0, 0, 0.4); + padding: 5%; + border-radius: 5px; + position: fixed; + z-index: 3000; + display: flex; + flex-direction: column; + width: 90%; +} + +/*Tabs*/ +.tabs { + width: 100%; + float: none; + list-style: none; + position: relative; + text-align: left; +} + +.tabs li { + float: left; + display: block; + width: 50%; +} + +.tabs input[type="radio"] { + position: absolute; + top: 0; + left: -9999px; +} + +.tabs label { + display: block; + padding: 14px 21px; + border-radius: 1px 1px 0 0; + font-size: 1em; + font-family: "Roboto",sans-serif; + letter-spacing: 1px; + font-weight: normal; + text-align: center; + background: #fff; + cursor: pointer; + position: relative; + top: 4px; + width: 100%; + -moz-transition: all 0.2s ease-in-out; + -o-transition: all 0.2s ease-in-out; + -webkit-transition: all 0.2s ease-in-out; + transition: all 0.2s ease-in-out; +} + +.tabs label:hover { + background: linear-gradient(336.74deg, #c80a48 1.46%, #48020d 85.12%); + color: #fff; +} + +.tabs .tab-content { + z-index: 2; + display: none; + overflow: hidden; + width: 100%; + font-size: 17px; + line-height: 25px; + padding: 25px; + position: absolute; + top: 53px; + left: 0; + background: #fce7e7; + border-radius: 10px; +} + +.tabs [id^="tab"]:checked + label { + top: 0; + padding-top: 17px; + background: linear-gradient(336.74deg, #c80a48 1.46%, #48020d 85.12%); + color: #fff; +} + +.tabs [id^="tab"]:checked ~ [id^="tab-content"] { + display: block; +} + +/*#text-box { + border-radius: 8px 8px 8px 8px; + width: 100%; + height: 95%; + resize: none; +}*/ + +/*Card modal*/ +.card-modal { + display: none; + position: fixed; + z-index: 100; + left: 0; + top: 0; + width: 100%; /* Full width */ + height: 100%; /* Full height */ + overflow: auto; /* Enable scroll if needed */ + background-color: rgb(0, 0, 0); /* Fallback color */ + background-color: rgba(0, 0, 0, 0.4); /* Black w/ opacity */ +} + +.modal{ + font-family: "Roboto",sans-serif; + color: #040404; + font-size: 18px; + text-align: center; + background-color: #fff; + box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); + padding: 30px; + position: fixed; + z-index: 3000; + overflow: auto; + min-height: 250px; + border-radius: 0px 35px 0px 35px; + border: 2px solid #8a0829; + +} + + +/*Media querys*/ +@media screen and (min-width: 450px) { + .my-header { + border-radius: 0px; + } + + .modal { + width: 70%; /* Could be more or less, depending on screen size */ + margin: 30% auto; /* 15% from the top and centered */ + } +} + +@media screen and (min-width: 500px) { + .col-8 { + width: 60%; + padding: 0px 15px; + } + .col-6 { + width: 50%; + padding: 0px 15px 0px 0px; + } + + .col-4 { + width: 40%; + padding: 0px 15px; + } + + .text-box { + display: flex; + justify-content: flex-start; + } + + .img { + width: 100%; + height: auto; + } + + .col-4, + .col-8 { + float: right; + } + .card-img-bottom { + width: 100%; + height: auto; + } + .desktop-view { + display: contents; + } + + ul .mobile-view { + display: none; + width: 100%; + } + .tabs li { + width: 100%; + } + + .modal { + width: 35%; /* Could be more or less, depending on screen size */ + margin: 15% auto; /* 15% from the top and centered */ + } +} diff --git a/src/utils/countLikes.js b/src/utils/countLikes.js new file mode 100644 index 00000000..c711b036 --- /dev/null +++ b/src/utils/countLikes.js @@ -0,0 +1,39 @@ +import { userSigned } from '../index.js'; +export let addLikes = (e) => { + let idPost; + idPost = e.target.name; + const docRef = firebase.firestore().collection('publications').doc(idPost); + docRef.get().then((doc) => { + if (doc.exists) { + const arrayLikes = doc.data(); + console.log(arrayLikes); + + if (arrayLikes.addLikes.includes(userSigned.uid)) { + console.log('you already add a like'); + firebase + .firestore() + .collection('publications') + .doc(idPost) + .update({ + likes: firebase.firestore.FieldValue.increment(-1), + addLikes: firebase.firestore.FieldValue.arrayRemove(userSigned.uid), + }); + } else { + console.log('this is your first like'); + firebase + .firestore() + .collection('publications') + .doc(idPost) + .update({ + likes: firebase.firestore.FieldValue.increment(+1), + addLikes: firebase.firestore.FieldValue.arrayUnion(userSigned.uid), + }); + } + } else { + // doc.data() will be undefined in this case + console.log('No such document!'); + } + }).catch((error) => { + console.log('Error getting document:', error); + }); +}; diff --git a/src/utils/createUsers.js b/src/utils/createUsers.js new file mode 100644 index 00000000..c775f59d --- /dev/null +++ b/src/utils/createUsers.js @@ -0,0 +1,35 @@ +/* eslint no-param-reassign: "error" */ +function createUser(emailNew, password) { + firebase + .auth() + .createUserWithEmailAndPassword(emailNew, password) + .catch((error) => { + const errorCode = error.code; + const errorMessage = error.message; + switch (errorCode) { + case 'auth/invalid-email': + alert(`Correo lisiado ${errorMessage}`); + break; + case 'auth/weak-password': + alert(`por tu seguridad usa una contraseña más larga ${errorMessage}`); + break; + default: + alert('ingreso lisiado'); + } + }); +} +export function printCreate(printCreateUser1, userAction2) { + printCreateUser1.style.display = 'none'; + const register = `
+


+


+

+
`; + userAction2.innerHTML = register; + const emailNew = document.getElementById('emailNw'); + const password = document.getElementById('passwordNw'); + const createNewUser = document.getElementById('createUserNw'); + createNewUser.addEventListener('click', () => { + createUser(emailNew.value, password.value); + }); +} diff --git a/src/utils/deletePost.js b/src/utils/deletePost.js new file mode 100644 index 00000000..f2587b07 --- /dev/null +++ b/src/utils/deletePost.js @@ -0,0 +1,10 @@ +//* *******************delete comments**************************************** +export const actionDelete = (e) => { + firebase.firestore().collection('publications').doc(e.target.id).delete() + .then(() => { + console.log('Document successfully deleted!'); + }) + .catch((error) => { + console.error('Error removing document: ', error); + }); +}; diff --git a/src/utils/exit.js b/src/utils/exit.js new file mode 100644 index 00000000..47f2dea9 --- /dev/null +++ b/src/utils/exit.js @@ -0,0 +1,10 @@ +//* *******************user singout***************************** +export function signOut() { + firebase.auth().signOut() + .then(() => { + console.log('Out'); + }) + .catch((error) => { + console.log(error); + }); +} diff --git a/src/utils/info.js b/src/utils/info.js new file mode 100644 index 00000000..1fab32c9 --- /dev/null +++ b/src/utils/info.js @@ -0,0 +1,19 @@ +export function openModal() { + const divInfoModal = document.createElement('div'); + divInfoModal.className = 'modal'; + divInfoModal.id = 'modalInfo'; + document.body.appendChild(divInfoModal); + const textModal = 'Creamos esta comunidad para que compartas' + + ' y encuentres algo que te pueda ayudar en tu examén de admisión. ' + + 'No estudies solo, juntos podemos lograrlo...' + + '\n ~La técnica al servicio de la Patria~ \n \n'; + const btClose = document.createElement('button'); + btClose.className = 'btnStyles'; + btClose.id = 'btnCloseM'; + btClose.innerHTML = 'Cerrar'; + divInfoModal.innerText = textModal; + divInfoModal.appendChild(btClose); + btClose.addEventListener('click', () => { + document.body.removeChild(divInfoModal); + }); +} diff --git a/src/utils/loginEmail.js b/src/utils/loginEmail.js new file mode 100644 index 00000000..cd2f9496 --- /dev/null +++ b/src/utils/loginEmail.js @@ -0,0 +1,22 @@ +//* **************login register users******************************** +export function login(email, passLogin) { + if (!email || !passLogin) return 'esta mal lisiada'; + if (passLogin.length < 6) return 'no cumple lisiada'; + return firebase.auth().signInWithEmailAndPassword(email, passLogin) + .catch((error) => { + const errorCode = error.code; + const errorMessage = error.message; + switch (errorCode) { + case 'auth/invalid-email': + alert(`Correo electrónico incorrecto ${errorMessage}`); + console.log(errorCode); + break; + case 'auth/wrong-password': + alert(`Verifica tu contraseña ${errorMessage}`); + console.log(errorCode); + break; + default: + alert('sigue participando '); + } + }); +} diff --git a/src/utils/modalEdit.js b/src/utils/modalEdit.js new file mode 100644 index 00000000..ec9093b8 --- /dev/null +++ b/src/utils/modalEdit.js @@ -0,0 +1,39 @@ +let divFormUpdate; +const actionEdit = (e) => { + console.log(e.target.value); + const comment = document.getElementById('newComment').value; + console.log(comment); + return firebase.firestore().collection('publications').doc(e.target.value).update({ + comments: comment, + }) + .then(() => { + console.log('Document successfully written!'); + document.body.removeChild(divFormUpdate); + }) + .catch((error) => { + console.error('Error writing document: ', error); + }); +}; + +const closed = () => { + document.body.removeChild(divFormUpdate); +}; +export function openModalEdit(e) { + const id = e.target.getAttribute('data-doc'); + divFormUpdate = document.createElement('div'); + divFormUpdate.className = 'viewFormEdit'; + divFormUpdate.id = 'formUpdate'; + document.body.appendChild(divFormUpdate); + firebase.firestore().collection('publications').doc(id).get() + .then((consultDb) => { + const doc = consultDb.data(); + const divUpdate = ` +

+

`; + divFormUpdate.innerHTML = divUpdate; + const btnUpdate = document.querySelectorAll('.btnSty'); + btnUpdate.forEach(update => update.addEventListener('click', actionEdit)); + const btnClose = document.querySelectorAll('.btnStylesC'); + btnClose.forEach(close => close.addEventListener('click', closed)); + }); +} diff --git a/src/utils/openComment.js b/src/utils/openComment.js new file mode 100644 index 00000000..a00dc70e --- /dev/null +++ b/src/utils/openComment.js @@ -0,0 +1,52 @@ +import { getName,getNameProfile } from '../index.js'; +let idAddComent; +let idAddComentDiv; +export function openModalComent(e) { + idAddComent = e.target.getAttribute('data-id'); + idAddComentDiv = e.target.getAttribute('title');; + console.log(idAddComent); + let divFormUpdate = document.createElement('div'); + divFormUpdate.className = 'viewFormEdit'; + divFormUpdate.id = 'formUpdate'; + document.body.appendChild(divFormUpdate); + firebase.firestore().collection('publications').doc(idAddComent).get() + .then((consultDb) => { + const doc = consultDb.data(); + const divUpdate = ` +

+

`; + divFormUpdate.innerHTML = divUpdate; + const btnAddC= document.querySelectorAll('.btnAddC'); + btnAddC.forEach(update => update.addEventListener('click', actionComent)); + const btnClose = document.querySelectorAll('.btnStylesC'); + btnClose.forEach(close => close.addEventListener('click', closedModal)); + + }); + const closedModal = () => { + document.body.removeChild(divFormUpdate); + }; + } + +const actionComent = (e) => { + console.log(getName); + let nwC= document.getElementById('newComment').value; + console.log(nwC); + firebase.firestore().collection('coments').add({ + opinion: nwC, + idC: e.target.value + }) + .then((docRef) => { + console.log('Document written with ID: ', docRef.id); + + firebase + .firestore() + .collection('publications') + .doc(idAddComent) + .update({ + addComent: firebase.firestore.FieldValue.arrayUnion(" "+ getNameProfile()+": " + nwC) + }); + }) + .catch((error) => { + console.error('Error adding document: ', error); + }); + } diff --git a/src/utils/providers.js b/src/utils/providers.js new file mode 100644 index 00000000..17609351 --- /dev/null +++ b/src/utils/providers.js @@ -0,0 +1,10 @@ +//* ****************login with socialmedia************************** +export function actionGoogle() { + const providerGoogle = new firebase.auth.GoogleAuthProvider(); + return firebase.auth().signInWithPopup(providerGoogle); +} + +export function actionFacebook() { + const providerFacebook = new firebase.auth.FacebookAuthProvider(); + return firebase.auth().signInWithPopup(providerFacebook); +} diff --git a/src/utils/saveInFirestore.js b/src/utils/saveInFirestore.js new file mode 100644 index 00000000..4048b1a9 --- /dev/null +++ b/src/utils/saveInFirestore.js @@ -0,0 +1,28 @@ +export function saveFirestore(name, img, data, link) { + let date = new Date(); + date += Date.now(); + const date1 = date.slice(0, 25); + const coment = document.querySelector('#text-box'); + console.log(`post ${coment.value}`); + //* ******************add comments in firestore********************************* + data.collection('publications').add({ + name, + photo: img, + date: date1, + comments: coment.value, + likes: '', + image: link || '', + addLikes: '', + addComent:'', + }) + .then((docRef) => { + console.log('Document written with ID: ', docRef.id); + console.log(coment.value); + const cleanBox = document.getElementById('text-box'); + cleanBox.value = ''; + console.log(cleanBox); + }) + .catch((error) => { + console.error('Error adding document: ', error); + }); +} diff --git a/storage.rules b/storage.rules new file mode 100644 index 00000000..d494542e --- /dev/null +++ b/storage.rules @@ -0,0 +1,7 @@ +service firebase.storage { + match /b/{bucket}/o { + match /{allPaths=**} { + allow read, write: if request.auth!=null; + } + } +} diff --git a/test/example.spec.js b/test/example.spec.js deleted file mode 100644 index 1aca05e9..00000000 --- a/test/example.spec.js +++ /dev/null @@ -1,7 +0,0 @@ -import { example } from '../src/example.js'; - -describe('example', () => { - it('debería ser una función', () => { - expect(typeof example).toBe('function'); - }); -}); \ No newline at end of file diff --git a/test/loginEmail.spec.js b/test/loginEmail.spec.js new file mode 100644 index 00000000..0369f775 --- /dev/null +++ b/test/loginEmail.spec.js @@ -0,0 +1,30 @@ +import * as auth from '../src/utils/loginEmail.js'; + +global.firebase = { + auth: jest.fn(() => ({ + signInWithEmailAndPassword: jest.fn(() => new Promise((resolve, reject) => { + resolve(true); + console.log(reject); + })), + })), +}; + +test('Password min 6 characters', () => { + const email = 'dany26@hotmail.com'; + const password = 'paña'; + expect(auth.login(email, password)).toBe('no cumple lisiada'); +}); + +test('format email incorrect', () => { + const email = 'dany26@h'; + expect(auth.login(email)).toBe('esta mal lisiada'); +}); + + +test('User correct', () => { + const email = 'danycl_26@hotmail.com'; + const password = 'patricio'; + auth.login(email, password).then((ok) => { + expect(ok).toBe(true); + }); +}); diff --git a/test/testSaveFirestore.spec.js b/test/testSaveFirestore.spec.js new file mode 100644 index 00000000..abfc6dbd --- /dev/null +++ b/test/testSaveFirestore.spec.js @@ -0,0 +1,31 @@ +import MockFirebase from 'mock-cloud-firestore'; + +import { saveFirestore } from '../src/utils/saveInFirestore.js'; +import { addNewCard } from '../src/index.js'; + +const fixtureData = { + __collection__: { + publications: { + __doc__: { + abc1d: { + title: 'Hola, encontre esto que puede ayudarnos', + complete: false, + }, + }, + }, + }, +}; + +global.firebase = new MockFirebase(fixtureData, { isNaiveSnapshotListenerEnabled: true }); + + +describe('lista de notas', () => { + it('Debería porder publicar', done => saveFirestore('preparar la pildora') + .then(() => addNewCard( + (data) => { + const result = data.find(note => note.title === 'Hola, encontre esto que puede ayudarnos'); + expect(result.title).toBe('Hola, encontre esto que puede ayudarnos'); + done(); + }, + ))); +});