diff --git a/README.md b/README.md index c737b2cd..fd062ad0 100644 --- a/README.md +++ b/README.md @@ -1,530 +1,79 @@ # Creando una Red Social +##Desarrollado por Laura Garduño, Laura Buitrago y Sandra Corral. + ## Índice -* [1. Preámbulo](#1-preámbulo) +* [1. Introducción al proyecto](#1-Introducción-al-proyecto) * [2. Resumen del proyecto](#2-resumen-del-proyecto) -* [3. Objetivos de aprendizaje](#3-objetivos-de-aprendizaje) -* [4. Consideraciones generales](#4-consideraciones-generales) -* [5. Criterios de aceptación mínimos del proyecto](#5-criterios-de-aceptación-mínimos-del-proyecto) -* [6. Hacker edition](#6-hacker-edition) -* [7. Entrega](#7-entrega) -* [8. Pistas, tips y lecturas complementarias](#8-pistas-tips-y-lecturas-complementarias) - -## 1. Preámbulo - -Instagram, Snapchat, Twitter, Facebook, Twitch, Linkedin, etc. Las redes -sociales han invadido nuestras vidas. Las amamos u odiamos, y muchos no podemos -vivir sin ellas. - -![adem-ay-Tk9m_HP4rgQ-unsplash](https://user-images.githubusercontent.com/110297/135544666-4efa54f1-4ff6-4c4c-b398-6df04ef56117.jpg) - -Hay redes sociales de todo tipo y para todo tipo de intereses. Por ejemplo, -en una ronda de financiamiento con inversionistas, se presentó una red social -para químicos en la que los usuarios podían publicar artículos sobre sus -investigaciones, comentar en los artículos de sus colegas, y filtrar artículos -de acuerdo a determinadas etiquetas o su popularidad, lo más reciente, o lo -más comentado. - -## 2. Resumen del proyecto - -En este proyecto construirás una Red Social sobre lo que decidan tú y tu equipo. -Podría ser, por ejemplo, sobre alimentación saludable, feminismo, educación, -salud, energías renovables, amantes de las [Empanadas](https://es.wikipedia.org/wiki/Empanada) -o de los [Tacos de Canasta](https://es.wikipedia.org/wiki/Taco), -de la [Feijoada](https://es.wikipedia.org/wiki/Feijoada), o de lo que sea. - -Tu Red Social tendrá que permitir a cualquier usuario crear una cuenta de acceso -y loguearse con ella; crear, editar, borrar y _"likear"_ publicacciones. - -Por lo tanto, en este proyecto construirás una -[Single-page Application (SPA)](https://es.wikipedia.org/wiki/Single-page_application) -[_responsive_](https://curriculum.laboratoria.la/es/topics/css/02-responsive) (con más de una vista / página) -en la que podamos **leer y escribir datos**. - -### Los objetivos generales de este proyecto son los siguientes - -* Desarrollar una SPA con temática de red social -* Aplicar los conceptos de responsividad en el desarrollo de las vistas (templates) -* Implementar un router para la navegación entre las diferentes vistas de la aplicación -* Emplear un servicio externo para la persistencia de datos de la aplicación -* Crear una suite de pruebas unitarias que permitan testear código asíncrono - -Para lograr estos objetivos, deberás aprender y hacer uso de las siguientes -herramientas o habilidades técnicas: - -## 3. Objetivos de aprendizaje - -Reflexiona y luego marca los objetivos que has llegado a entender y aplicar en tu proyecto. Piensa en eso al decidir tu estrategia de trabajo. - -### HTML - -- [ ] **Uso de HTML semántico** - -
Links

- - * [HTML semántico](https://curriculum.laboratoria.la/es/topics/html/02-html5/02-semantic-html) - * [Semantics - MDN Web Docs Glossary](https://developer.mozilla.org/en-US/docs/Glossary/Semantics#Semantics_in_HTML) -

- -### CSS - -- [ ] **Uso de selectores de CSS** - -
Links

- - * [Intro a CSS](https://curriculum.laboratoria.la/es/topics/css/01-css/01-intro-css) - * [CSS Selectors - MDN](https://developer.mozilla.org/es/docs/Web/CSS/CSS_Selectors) -

- -- [ ] **Modelo de caja (box model): borde, margen, padding** - -
Links

- - * [Box Model & Display](https://curriculum.laboratoria.la/es/topics/css/01-css/02-boxmodel-and-display) - * [The box model - MDN](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/The_box_model) - * [Introduction to the CSS box model - MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Introduction_to_the_CSS_box_model) - * [CSS display - MDN](https://developer.mozilla.org/pt-BR/docs/Web/CSS/display) - * [display - CSS Tricks](https://css-tricks.com/almanac/properties/d/display/) -

- -- [ ] **Uso de flexbox en CSS** - -
Links

- - * [A Complete Guide to Flexbox - CSS Tricks](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) - * [Flexbox Froggy](https://flexboxfroggy.com/#es) - * [Flexbox - MDN](https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Flexbox) -

- -- [ ] **Uso de CSS Grid Layout** - -
Links

- - * [A Complete Guide to Grid - CSS Tricks](https://css-tricks.com/snippets/css/complete-guide-grid/) - * [Grids - MDN](https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Grids) -

- -### Web APIs - -- [ ] **Uso de selectores del DOM** - -
Links

- - * [Manipulación del DOM](https://curriculum.laboratoria.la/es/topics/browser/02-dom/03-1-dom-methods-selection) - * [Introducción al DOM - MDN](https://developer.mozilla.org/es/docs/Web/API/Document_Object_Model/Introduction) - * [Localizando elementos DOM usando selectores - MDN](https://developer.mozilla.org/es/docs/Web/API/Document_object_model/Locating_DOM_elements_using_selectors) -

- -- [ ] **Manejo de eventos del DOM (listeners, propagación, delegación)** - -
Links

- - * [Introducción a eventos - MDN](https://developer.mozilla.org/es/docs/Learn/JavaScript/Building_blocks/Events) - * [EventTarget.addEventListener() - MDN](https://developer.mozilla.org/es/docs/Web/API/EventTarget/addEventListener) - * [EventTarget.removeEventListener() - MDN](https://developer.mozilla.org/es/docs/Web/API/EventTarget/removeEventListener) - * [El objeto Event](https://developer.mozilla.org/es/docs/Web/API/Event) -

- -- [ ] **Manipulación dinámica del DOM** - -
Links

- - * [Introducción al DOM](https://developer.mozilla.org/es/docs/Web/API/Document_Object_Model/Introduction) - * [Node.appendChild() - MDN](https://developer.mozilla.org/es/docs/Web/API/Node/appendChild) - * [Document.createElement() - MDN](https://developer.mozilla.org/es/docs/Web/API/Document/createElement) - * [Document.createTextNode()](https://developer.mozilla.org/es/docs/Web/API/Document/createTextNode) - * [Element.innerHTML - MDN](https://developer.mozilla.org/es/docs/Web/API/Element/innerHTML) - * [Node.textContent - MDN](https://developer.mozilla.org/es/docs/Web/API/Node/textContent) -

- -- [ ] **Ruteado (History API, evento hashchange, window.location)** - -
Links

- - * [Manipulando el historial del navegador - MDN](https://developer.mozilla.org/es/docs/DOM/Manipulando_el_historial_del_navegador) -

- -### JavaScript - -- [ ] **Arrays (arreglos)** - -
Links

- - * [Arreglos](https://curriculum.laboratoria.la/es/topics/javascript/04-arrays) - * [Array - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/) - * [Array.prototype.sort() - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) - * [Array.prototype.forEach() - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) - * [Array.prototype.map() - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/map) - * [Array.prototype.filter() - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) - * [Array.prototype.reduce() - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) -

- -- [ ] **Objetos (key, value)** - -
Links

- - * [Objetos en JavaScript](https://curriculum.laboratoria.la/es/topics/javascript/05-objects/01-objects) -

- -- [ ] **Diferenciar entre tipos de datos primitivos y no primitivos** - -- [ ] **Variables (declaración, asignación, ámbito)** - -
Links

- - * [Valores, tipos de datos y operadores](https://curriculum.laboratoria.la/es/topics/javascript/01-basics/01-values-variables-and-types) - * [Variables](https://curriculum.laboratoria.la/es/topics/javascript/01-basics/02-variables) -

- -- [ ] **Uso de condicionales (if-else, switch, operador ternario, lógica booleana)** - -
Links

- - * [Estructuras condicionales y repetitivas](https://curriculum.laboratoria.la/es/topics/javascript/02-flow-control/01-conditionals-and-loops) - * [Tomando decisiones en tu código — condicionales - MDN](https://developer.mozilla.org/es/docs/Learn/JavaScript/Building_blocks/conditionals) -

- -- [ ] **Uso de bucles/ciclos (while, for, for..of)** - -
Links

- - * [Bucles (Loops)](https://curriculum.laboratoria.la/es/topics/javascript/02-flow-control/02-loops) - * [Bucles e iteración - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Guide/Loops_and_iteration) -

- -- [ ] **Funciones (params, args, return)** - -
Links

- - * [Funciones (control de flujo)](https://curriculum.laboratoria.la/es/topics/javascript/02-flow-control/03-functions) - * [Funciones clásicas](https://curriculum.laboratoria.la/es/topics/javascript/03-functions/01-classic) - * [Arrow Functions](https://curriculum.laboratoria.la/es/topics/javascript/03-functions/02-arrow) - * [Funciones — bloques de código reutilizables - MDN](https://developer.mozilla.org/es/docs/Learn/JavaScript/Building_blocks/Functions) -

- -- [ ] **Pruebas unitarias (unit tests)** - -
Links

- - * [Empezando con Jest - Documentación oficial](https://jestjs.io/docs/es-ES/getting-started) -

- -- [ ] **Pruebas asíncronas** - -
Links

- - * [Tests de código asincrónico con Jest - Documentación oficial](https://jestjs.io/docs/es-ES/asynchronous) -

- -- [ ] **Uso de mocks y espías** - -
Links

- - * [Manual Mocks con Jest - Documentación oficial](https://jestjs.io/docs/es-ES/manual-mocks) -

- -- [ ] **Módulos de ECMAScript (ES Modules)** - -
Links

- - * [import - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Statements/import) - * [export - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Statements/export) -

- -- [ ] **Uso de linter (ESLINT)** - -- [ ] **Uso de identificadores descriptivos (Nomenclatura y Semántica)** - -- [ ] **Diferenciar entre expresiones (expressions) y sentencias (statements)** - -- [ ] **Callbacks** - -
Links

- - * [Función Callback - MDN](https://developer.mozilla.org/es/docs/Glossary/Callback_function) -

- -- [ ] **Promesas** - -
Links

- - * [Promise - MDN](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Promise) - * [How to Write a JavaScript Promise - freecodecamp (en inglés)](https://www.freecodecamp.org/news/how-to-write-a-javascript-promise-4ed8d44292b8/) -

- -### Control de Versiones (Git y GitHub) - -- [ ] **Git: Instalación y configuración** - -- [ ] **Git: Control de versiones con git (init, clone, add, commit, status, push, pull, remote)** - -- [ ] **Git: Integración de cambios entre ramas (branch, checkout, fetch, merge, reset, rebase, tag)** - -- [ ] **GitHub: Creación de cuenta y repos, configuración de llaves SSH** - -- [ ] **GitHub: Despliegue con GitHub Pages** - -
Links

- - * [Sitio oficial de GitHub Pages](https://pages.github.com/) -

- -- [ ] **GitHub: Colaboración en Github (branches | forks | pull requests | code review | tags)** - -- [ ] **GitHub: Organización en Github (projects | issues | labels | milestones | releases)** - -### Centrado en el usuario - -- [ ] **Diseñar y desarrollar un producto o servicio poniendo a las usuarias en el centro** - -### Diseño de producto - -- [ ] **Crear prototipos de alta fidelidad que incluyan interacciones** - -- [ ] **Seguir los principios básicos de diseño visual** - -### Investigación - -- [ ] **Planear y ejecutar testeos de usabilidad de prototipos en distintos niveles de fidelidad** - -
Links

- - * [Intro a testeos usabilidad](https://coda.io/@bootcamp-laboratoria/contenido-ux/test-de-usabilidad-15) - * [Pruebas con Usuarios 1 — ¿Qué, cuándo y para qué testeamos?](https://eugeniacasabona.medium.com/pruebas-con-usuarios-1-qu%C3%A9-cu%C3%A1ndo-y-para-qu%C3%A9-testeamos-7c3a89b4b5e7) -

- -### Firebase - -- [ ] **Firebase Auth** +* [3. Prototipos](#3-objetivos-de-aprendizaje) +* [4. Historias de usuario](#4-historias-de-usuario) +* [5. Comportamiento UI](#5-comportamiento-UI) +* [6. Producto final](#6-producto-final) +* [7. Pruebas unitarias](#7-pruebas-unitarias) -
Links

- * [Primeros pasos con Firebase Authentication en sitios web - Documentación oficial](https://firebase.google.com/docs/auth/web/start?hl=es) - * [Administra usuarios en Firebase (onAuthStateChanged)](https://firebase.google.com/docs/auth/web/manage-users?hl=es#get_the_currently_signed-in_user) -

+## 1. Introducción al proyecto: -- [ ] **Firestore** +En este proyecto nos propusimos desarrollar una red social para los amantes de los libros. Creamos un espacio para que los usuarios creen su perfil y puedan compartir reseñas de los libros que han leído y así disfrutar de dar y responder a diferentes opiniones. https://dev-002-social-network-readingclub.vercel.app/ -
Links

+![Logo1](src/images/logo.png) - * [Firestore - Documentación oficial](https://firebase.google.com/docs/firestore?hl=es) - * [Reglas de seguridad de Firestore - Documentación oficial](https://firebase.google.com/docs/rules?hl=es) - * [Obtén actualizaciones en tiempo real con Cloud Firestore - Documentación oficial](https://firebase.google.com/docs/firestore/query-data/listen?hl=es) -

-## 4. Consideraciones generales +## 2. Resumen del proyecto: -* Este proyecto se debe trabajar en equipos de tres. +En este proyecto construimos una red social con la siguiente información: -* La lógica del proyecto debe estar implementada completamente en JavaScript - (ES6+), HTML y CSS :smiley:. Para este proyecto **no está permitido** utilizar - _frameworks_ o librerías de CSS y JS. +* Desarrollamos una SPA que cuenta con los conceptos responsive. +* Implementamos un servicio externo a través de Firebase. +* Creamos e implementamos pruebas unitarias. -* La división y organización del trabajo debe permitir, sin excepciones, que - **cada integrante** del equipo practique el aprendizaje de todo lo involucrado - en **cada historia**. _No se dividan el trabajo como en una fábrica._ - - ¿Hasta acá has avanzado en tus proyectos con cierta fluidez y sin mayores - problemas? Sé generosa con tus compañeras, permíteles aprender y practicar - sin restricciones, aunque tome un poco más de tiempo. Aproveha de - _coachearlas_, de hacer _pair programming_, una de las mejores maneras de - aprender es explicando verbalmente. - - ¿Se te está haciendo difícil y te cuesta un poco más avanzar? No te quedes - con las partes "fáciles" del proyecto, conversa, negocia, exige tu oportunidad - para practicar y aprender lo que se te hace más difícil. +## 3. Prototipos: -* Solamente pueden trabajar en una única historia por vez, no pueden avanzar a - la siguiente sin haber completado la anterior. La historia se completa cuando - se cumplen **todos** sus Criterios de Aceptación + **toda** su Definición - de Terminado. +Creamos prototipos de baja y de alta fidelidad utilizando Figma y basándonos en las historias de usuario logramos establecer el diseño de la red social. +Obtuvimos feedback de pares y coaches hasta que consideramos que la página estaba lista para desarrollarla. -Para comenzar tendrás que hacer un _fork_ y _clonar_ este repositorio. +### Prototipo de baja fidelidad +![Prototipobaja1](src/images/prototipobaja.jpg) -## 5. Criterios de aceptación mínimos del proyecto +### Prototipo de alta fidelidad +![Prototipoalta1](src/images/Prototipo1.png) +![Prototipoalta2](src/images/Prototipo2.png) +![Prototipoalta3](src/images/Prototipo3.png) -### 5.1 Boilerplate -Este proyecto no incluye un _boilerplate_, así es que tendrás que definir la -estructura de carpetas y escribir tus propias Pruebas Unitarias (_tests_). Para -hacerlo, puedes guiarte de los proyectos anteriores. -### 5.2 Definición del producto +## 4. Historias de usuario: -En el `README.md` cuéntanos brevemente cómo descubriste las necesidades de los -usuarios y cómo llegaste a la definición final de tu producto. Es importante -que detalles: +* Historia de usuario 1: Yo como fanática de los libros quiero crear una cuenta en ReadingClub para conocer y dar diferentes opiniones de libros y así compartir reseñas de libros y conocer las opiniones de otras personas. +* Historia de usuario 2: Yo como usuario de ReadingClub quiero publicar para dar mi opinión acerca de diferentes libros. +* Historia de usuario 3: Yo como usuario que ya publicó una reseña quiero leer las diferentes reseñas de otros usuarios en la plataforma. +* Historia de usuario 4: Yo como usuario que ya está logeado en la plataforma quiero poder cerrar sesión para que otros usuarios puedan ingresar para conocer nuevos libros. -* Quiénes son los principales usuarios de producto. -* Qué problema resuelve el producto / para qué le servirá a estos usuarios. -### 5.3 Historias de usuario +## 5. Comportamiento UI -Una vez que entiendas las necesidades de tus usuarixs, escribe las Historias de -Usuario que representen todo lo que necesitan hacer/ver en la Red Social. Cada -una de tus Historias de Usuario debe tener: +### Creación de cuenta de usuario: + +* Ingreso a través de data almacenada en Firebase. +* Creación de cuentas de usuario con correo y contraseña y a través de cuentas de Google. -* **Criterios de Aceptación:** todo lo que debe ocurrir para satisfacer las - necesidades del usuario. +### Dashboard: -* **Definición de terminado:** todos los aspectos técnicos que deben cumplirse - para que, como equipo, sepan que esa historia está terminada y lista - para publicarse. **Todas** tus Historias de Usuario (salvo excepciones), deben - incluir estos aspectos en su Definición de Terminado (más todo lo que - necesiten agregar): +* Las publicaciones en el muro son en tiempo real. +* Se pueden editar las publicaciones propias. +* Se puede dar like a las publicaciones y ver el conteo de likes. +* Se pueden eliminar las publicaciones propias y se pide confirmación para eliminar. - - Debe ser una SPA. - - Debe ser _responsive_. - - Deben haber recibido _code review_ de al menos una compañera de otro equipo. - - Hicieron los _test_ unitarios - - Testearon manualmente buscando errores e imperfecciones simples. - - Hicieron _pruebas_ de usabilidad e incorporaron el _feedback_ de los - usuarios como mejoras. - - Desplegaron su aplicación y etiquetaron la versión (git tag). +## 6. Producto final: -### 5.4 Diseño de la Interfaz de Usuario (prototipo de baja fidelidad) +![Productofinal1](src/images/Final1.png) +![Productofinal2](src/images/final3.png) +![Productofinal3](src/images/Final2.png) -Debes definir cuál será el flujo que seguirá el usuario dentro de tu aplicación -y, con eso, diseña la Interfaz de Usuario (UI por sus siglas en inglés) que -siga este flujo. -### 5.5 Responsive +## 7. Pruebas unitarias: -Debe verse bien en dispositivos de pantallas grandes -(computadoras/es, laptops, etc.) y pequeñas (_tablets_, celulares, etc.). Te -sugerimos seguir la técnica de _`mobile first`_ (más detalles sobre esta técnica -al final). -### 5.6 Consideraciones del comportamiento de la interfaz de usuario (UI) - -Estas consideraciones te ayudarán a escribir las Definiciones de Terminado de -tus H.U.: -#### Creación de cuenta de usuario e inicio de sesión - -* _Login_ con Firebase: - - Para el _login_ y las publicaciones en el muro puedes utilizar [Firebase](https://firebase.google.com/products/database/) - - Creación de cuenta de acceso y autenticación con cuenta de correo y - contraseña, y también con una cuenta de Google. -* Validaciones: - - Solamente se permite el acceso a usuarios con cuentas válidas. - - No pueden haber usuarios repetidos. - - La cuenta de usuario debe ser un correo electrónico válido. - - Lo que se escriba en el campo (_input_) de contraseña debe ser secreto. -* Comportamiento: - - Al enviarse el formulario de registro o inicio de sesión, debe validarse. - - Si hay errores, se deben mostrar mensajes descriptivos para ayudar al - usuario a corregirlos. - -#### Muro/timeline - -* Validaciones: - - Al publicar, se debe validar que exista contenido en el _input_. -* Comportamiento: - - Al recargar la aplicación, se debe verificar si el usuario está _logueado_ - antes de mostrar contenido. - - Poder publicar un _post_. - - Poder dar y quitar _like_ a una publicación. Máximo uno por usuario. - - Llevar un conteo de los _likes_. - - Poder eliminar un post específico. - - Pedir confirmación antes de eliminar un _post_. - - Al dar _click_ para editar un _post_, debe cambiar el texto por un _input_ - que permita editar el texto y luego guardar los cambios. - - Al guardar los cambios debe cambiar de vuelta a un texto normal pero con la - información editada. - - Al recargar la página debo de poder ver los textos editados. - -### 5.7 Consideraciones técnicas Front-end - -* Separar la manipulación del DOM de la lógica (Separación de responsabilidades). -* Contar con múltiples vistas. Para esto, tu aplicación debe ser una - [Single Page Application (SPA)](https://es.wikipedia.org/wiki/Single-page_application) -* Alterar y persistir datos. Los datos que agregues o modifiques deberán - persistir a lo largo de la aplicación. Te recomendamos que uses - [Firebase](https://firebase.google.com/) para eso también. - -#### Pruebas unitarias (unit tests) - -* Recuerda que no hay un _setup_ de **tests** definido, dependerá de - la estructura de tu proyecto. Algo que no debes de olvidar es pensar en éstas - pruebas, te pueden ayudar a definir la estructura y nomenclatura de tu lógica. - -* Los tests unitarios deben cubrir un mínimo del 70% de _statements_, _functions_, - _lines_, y _branches_. - -### 5.8 Consideraciones técnicas UX - -* Hacer al menos 2 entrevistas con usuarios. -* Hacer un prototipo de baja fidelidad. -* Asegurarte de que la implementación en código siga los lineamientos del - diseño. -* Hacer sesiones de _testing de usabilidad_ con el producto en HTML. - -## 6. Hacker edition - -Las secciones llamadas _Hacker Edition_ son **opcionales**. Si **terminaste** -con todo lo anterior y te queda tiempo, intenta completarlas. Así podrás -profundizar y/o ejercitar más sobre los objetivos de aprendizaje del proyecto. - -* Permite crear posts con imágenes. -* Permite buscar usuarios, agregar y eliminar "amigos". -* Permite definir la privacidad de los _posts_ (público o solamente para amigos). -* Permite ver su muro de cualquier usuario "no-amigo" (solamente los - posts _públicos_). -* Permite comentar o responder una publicación. -* Permite editar perfil. - -## 7. Entrega - -El proyecto será _entregado_ subiendo tu código a GitHub (`commit`/`push`) y la -interfaz será desplegada usando GitHub pages u otro servicio de hosting que -puedas haber encontrado en el camino. - -*** - -## 8. Pistas, tips y Lecturas complementarias - -### 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 estas vistas. Este problema se puede -afrontar de muchas maneras: con archivos HTML independientes (cada uno con su -URL) y links tradicionales, manteniendo estado en memoria y rederizando -condicionalmente (sin refrescar la página), [manipulando el historial del -navegador](https://developer.mozilla.org/es/docs/DOM/Manipulando_el_historial_del_navegador) -con [`window.history`](https://developer.mozilla.org/es/docs/Web/API/Window/history). -En este proyecto te invitamos a explorar opciones y decidir una opción -de implementación. - -### 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 [Firebase](https://firebase.google.com/). - -Otras: - -* [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 Is NOT Mobile Only - Nielsen Norman Group](https://www.nngroup.com/articles/mobile-first-not-mobile-only/) diff --git a/package.json b/package.json index 0c72f6c8..e69d1b20 100644 --- a/package.json +++ b/package.json @@ -11,24 +11,25 @@ "license": "MIT", "scripts": { "htmlhint": "htmlhint dist/*.html ", - "eslint": "eslint --ext .js src/ test/", + "eslint:fix":"eslint --fix --ext. js src/ test/", "stylelint": "stylelint --aei src/**/*.css", - "pretest": "npm run htmlhint && npm run eslint && npm run stylelint", "test": "jest --coverage", - "start": "serve -s src/" + "start": "serve -s src/", + "deploy": "-d src" }, + "devDependencies": { "@babel/core": "^7.11.4", "@babel/preset-env": "^7.11.0", - "babel-jest": "^27.0.1", + "babel-jest": "^27.5.1", "eslint": "^8.3.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-plugin-import": "^2.18.2", "eslint-plugin-jest": "^25.3.0", "htmlhint": "^1.0.0", - "jest": "^27.0.1", + "jest": "^27.5.1", "regenerator-runtime": "^0.13.1", - "serve": "^13.0.2", + "serve": "^14.1.2", "stylelint": "^14.1.0", "stylelint-config-recommended": "^6.0.0" }, @@ -39,5 +40,8 @@ "createdAt": "2022-11-30T16:52:37.204Z", "version": "5.5.0", "commit": "51e941edf1cc991930aefd7dd9c406a7c43741c1" + }, + "dependencies": { + "firebase": "^9.15.0" } -} \ No newline at end of file +} diff --git a/responsive.txt b/responsive.txt new file mode 100644 index 00000000..ac6613f2 --- /dev/null +++ b/responsive.txt @@ -0,0 +1,6 @@ +/*Responsive*/ +.llamarImagenesdiv{ + display: flex; + flex: wrap; + gap: 20px; +} \ No newline at end of file diff --git a/src/_Mock_@firebase/auth.js b/src/_Mock_@firebase/auth.js new file mode 100644 index 00000000..c5d02c34 --- /dev/null +++ b/src/_Mock_@firebase/auth.js @@ -0,0 +1,8 @@ +export const signInWithPopup = () => Promise.resolve(); +export const getAuth = () => {}; // es un objeto vacío +export const GoogleAuthProvider = class {}; // son un tipo de objeto que contiene funciones + +export const signOut = () => Promise.resolve(); +export const signInWithEmailAndPassword = jest.fn(() => Promise.resolve()); + +export const createUserWithEmailAndPassword = Promise.resolve(); \ No newline at end of file diff --git a/src/images/1erlike.png b/src/images/1erlike.png new file mode 100644 index 00000000..0fb7f274 Binary files /dev/null and b/src/images/1erlike.png differ diff --git a/src/images/2dolike.png b/src/images/2dolike.png new file mode 100644 index 00000000..634bc994 Binary files /dev/null and b/src/images/2dolike.png differ diff --git a/src/images/Final1.png b/src/images/Final1.png new file mode 100644 index 00000000..3a6b0f7d Binary files /dev/null and b/src/images/Final1.png differ diff --git a/src/images/Final2.png b/src/images/Final2.png new file mode 100644 index 00000000..8972e2b8 Binary files /dev/null and b/src/images/Final2.png differ diff --git a/src/images/Prototipo1.png b/src/images/Prototipo1.png new file mode 100644 index 00000000..e5586b9d Binary files /dev/null and b/src/images/Prototipo1.png differ diff --git a/src/images/Prototipo2.png b/src/images/Prototipo2.png new file mode 100644 index 00000000..c728a6ba Binary files /dev/null and b/src/images/Prototipo2.png differ diff --git a/src/images/Prototipo3.png b/src/images/Prototipo3.png new file mode 100644 index 00000000..b04eedc3 Binary files /dev/null and b/src/images/Prototipo3.png differ diff --git a/src/images/btnGoogle.png b/src/images/btnGoogle.png new file mode 100644 index 00000000..510e6192 Binary files /dev/null and b/src/images/btnGoogle.png differ diff --git a/src/images/cerrar-sesion.png b/src/images/cerrar-sesion.png new file mode 100644 index 00000000..27c0a967 Binary files /dev/null and b/src/images/cerrar-sesion.png differ diff --git a/src/images/close.png b/src/images/close.png new file mode 100644 index 00000000..b865a199 Binary files /dev/null and b/src/images/close.png differ diff --git a/src/images/delete.png b/src/images/delete.png new file mode 100644 index 00000000..7f98700b Binary files /dev/null and b/src/images/delete.png differ diff --git a/src/images/editar.png b/src/images/editar.png new file mode 100644 index 00000000..9274a976 Binary files /dev/null and b/src/images/editar.png differ diff --git a/src/images/final3.png b/src/images/final3.png new file mode 100644 index 00000000..f079857a Binary files /dev/null and b/src/images/final3.png differ diff --git a/src/images/logo.png b/src/images/logo.png new file mode 100644 index 00000000..425e0ba3 Binary files /dev/null and b/src/images/logo.png differ diff --git a/src/images/logout.png b/src/images/logout.png new file mode 100644 index 00000000..c280d10a Binary files /dev/null and b/src/images/logout.png differ diff --git a/src/images/prototipobaja.jpg b/src/images/prototipobaja.jpg new file mode 100644 index 00000000..52d664ba Binary files /dev/null and b/src/images/prototipobaja.jpg differ diff --git a/src/images/user.png b/src/images/user.png new file mode 100644 index 00000000..83dc81d9 Binary files /dev/null and b/src/images/user.png differ diff --git a/src/index.html b/src/index.html index 788db3c9..13f6e57f 100644 --- a/src/index.html +++ b/src/index.html @@ -1,12 +1,21 @@ + - Document + + + Social Network + + +
+
+ + \ No newline at end of file diff --git a/src/lib/Components/_redirects b/src/lib/Components/_redirects new file mode 100644 index 00000000..f8243379 --- /dev/null +++ b/src/lib/Components/_redirects @@ -0,0 +1 @@ +/* /index.html 200 \ No newline at end of file diff --git a/src/lib/Components/dashboard.js b/src/lib/Components/dashboard.js new file mode 100644 index 00000000..bd3d8d64 --- /dev/null +++ b/src/lib/Components/dashboard.js @@ -0,0 +1,242 @@ +import { onNavigate } from '../../main.js'; +import { + submitPost, + logOut, + getAllPosts, + deletePost, + currentUserInfo, + getTask, + updateTask, + giveLike, + dislike, +} from '../index.js'; + +export const login = () => { + const divLogin = document.createElement('div'); + divLogin.setAttribute('id', 'div-login'); + const viewLogin = ` + +
+ logoReading + +
+
+
+
+
+ profile + +
+
+
+
+
+

Crear Reseña

+ +
+
+
+
+ profile +
+
+ + +
+
+
+
+
+ + `; + + divLogin.innerHTML = viewLogin; + + const divTimeLine = divLogin.querySelector('#timeline-posts'); + const inputPostText = divLogin.querySelector('#input-post'); + const btnPost = divLogin.querySelector('#btn-post'); + const btnLogout = divLogin.querySelector('#btn-sign-out'); + const btnCreatePost = divLogin.querySelector('#btn-input-modal'); + const btnExit = divLogin.querySelector('.btn-exit'); + const divModalBackground = divLogin.querySelector('#modal-background-post'); + const divModalContent = divLogin.querySelector('#modal-content-post'); + + // apertura visual del modal + const showModal = () => { + divModalBackground.style.display = 'flex'; + divModalContent.style.display = 'block'; + document.body.style.overflow = 'hidden'; + }; + // ocultar visual del modal + const closeModal = () => { + divModalBackground.style.display = 'none'; + divModalContent.style.display = 'none'; + document.body.style.overflow = 'visible'; + inputPostText.value = ''; + }; + // onclick detelePost + const deletePostListener = (event) => { + const postId = event.target.dataset.id; + const opcion = confirm('Desea eliminar el comentario?'); + if (opcion === false) { } else { + deletePost(postId); + } + refreshPosts(); + }; + + // onclick editarPost + const editPost = async (event) => { + const docToEdit = await getTask(event.target.dataset.id); + const docData = docToEdit.data(); + showModal(); + inputPostText.value = docData.postText; + btnPost.disabled = false; + btnPost.doc = docToEdit; + }; + // onclikc likedPost + const likedPost = async (event) => { + const doc = await getTask(event.target.dataset.id); + const docData = doc.data(); + + if (docData.likes.some((like) => like === currentUserInfo().uid)) { + dislike(doc.id).then((response) => { + refreshPosts(); + }); + } else { + giveLike(event.target.dataset.id) + .then((response) => { + refreshPosts(); + }) + .catch(); + } + }; + + // funcion que llama getDocs de firestore y re pinta los html elements para mostrar + const refreshPosts = () => { + getAllPosts().then((posts) => { + divTimeLine.innerHTML = ''; + posts.forEach(((post) => { + const postData = post.data(); + const uid = currentUserInfo().uid; + const divPostEntry = document.createElement('div'); + + const imgUser = document.createElement('img'); + const userName = document.createElement('h2'); + const userPostText = document.createElement('h2'); + const editIcon = document.createElement('img'); + const dateTimePost = document.createElement('h1'); + const deleteIcon = document.createElement('img'); + const likePost = document.createElement('img'); + const likeNumber = document.createElement('h3'); + + divPostEntry.className = 'timeLine-post'; + imgUser.setAttribute('src', 'images/user.png'); + imgUser.className = 'iconUser'; + imgUser.className = 'iconUser'; + userName.innerHTML = postData.user; + userName.className = 'user-name-post'; + userPostText.innerHTML = postData.postText; + deleteIcon.setAttribute('src', '/images/delete.png'); + deleteIcon.className = 'delete-icon'; + deleteIcon.setAttribute('data-id', post.id); + deleteIcon.onclick = deletePostListener; + userPostText.className = 'textPost'; + dateTimePost.innerHTML = new Date(post.data().createdDateTime.seconds * 1000).toLocaleString(); + dateTimePost.className = 'date-post'; + editIcon.setAttribute('data-id', post.id); + editIcon.onclick = editPost; + editIcon.setAttribute('src', 'images/editar.png'); + editIcon.className = 'icon-edit'; + likePost.className = 'primer-like'; + likePost.onclick = likedPost; + likePost.setAttribute('data-id', post.id); + likeNumber.className = 'like-number'; + + if (postData.likes.some((like) => like === uid)) { + likePost.setAttribute('src', '/images/2dolike.png'); + } else { + likePost.setAttribute('src', '/images/1erlike.png'); + } + + likeNumber.innerHTML = postData.likes.length; + + if (postData.uid === currentUserInfo().uid) { + divPostEntry.appendChild(userName); + userName.appendChild(imgUser); + divPostEntry.appendChild(userPostText); + userName.appendChild(dateTimePost); + userPostText.appendChild(editIcon); + userPostText.append(deleteIcon); + userPostText.appendChild(likePost); + userPostText.appendChild(likeNumber); + userPostText.appendChild(likeNumber); + } else { + divPostEntry.appendChild(userName); + userName.appendChild(imgUser); + divPostEntry.appendChild(userPostText); + userName.appendChild(dateTimePost); + userPostText.appendChild(likePost); + userPostText.appendChild(likeNumber); + userPostText.appendChild(likeNumber); + } + + divTimeLine.appendChild(divPostEntry); + btnPost.innerText = 'PUBLICAR'; + closeModal(); + })); + }); + }; + + btnPost.addEventListener('click', (event) => { + const doc = event.currentTarget.doc; + + if (doc) { + const docData = doc.data(); + docData.postText = inputPostText.value; + updateTask(doc.id, docData).then((response) => { + btnPost.doc = null; + closeModal(); + refreshPosts(); + }); + } else { + submitPost(inputPostText.value).then((response) => { + closeModal(); + refreshPosts(); + alert('Reseña creada', response); + }); + } + }); + + // aqui se manda llamar el getDocs al cargar la pagina en Dashboard + refreshPosts(); + // Funcion cerrar sesion + + btnLogout.addEventListener('click', () => { + logOut(onNavigate); + }); + + // Funcion crear post + + btnCreatePost.addEventListener('click', () => { + showModal(); + inputPostText.focus(); + }); + + // Listener cerrar modal + btnExit.addEventListener('click', () => closeModal()); + + // Funcion activacion boton publicar + inputPostText.addEventListener('keyup', () => { + const valueInput = inputPostText.value.trim(); // trim() metodo que no permite activar boton con espacio + if (valueInput === '') { + btnPost.disabled = true; + } else { + btnPost.disabled = false; + } + }); + + divLogin.append( + btnLogout, + ); + return divLogin; +}; diff --git a/src/lib/Components/home.js b/src/lib/Components/home.js new file mode 100644 index 00000000..6708e7a6 --- /dev/null +++ b/src/lib/Components/home.js @@ -0,0 +1,69 @@ +import { onNavigate } from '../../main.js'; +import { authLogin, signIn, signInGoogle } from '../index.js'; + +export const home = () => { + const divHome = document.createElement('div'); + divHome.setAttribute('class', 'div-home'); + const viewHome = ` + + +
+

Inicia sesión

+ + +
+
+ +
+
+ + `; + + divHome.innerHTML = viewHome; + + const buttonRegister = document.createElement('button'); + const buttonLogin = document.createElement('button'); + + buttonRegister.textContent = 'Crear cuenta'; + buttonLogin.textContent = 'Iniciar sesion'; + buttonRegister.setAttribute('id', 'btnRegister'); + buttonLogin.setAttribute('id', 'btnLogin'); + + buttonRegister.addEventListener('click', () => onNavigate('/register')); + + divHome.appendChild(buttonRegister); + divHome.appendChild(buttonLogin); + + const btnGoogle = divHome.querySelector('.btn-google'); + btnGoogle.addEventListener('click', () => { + signInGoogle(onNavigate); + }); + const btnLogin = divHome.querySelector('#btnLogin'); + btnLogin.addEventListener('click', () => { + const email = document.querySelector('#email').value; + const password = document.querySelector('#password').value; + + signIn(email, password) + .then(() => { + onNavigate('/dashboard'); + }) + .catch((error) => { + if (error.code === 'auth/invalid-email') { + document.querySelector('#errorLogin').innerHTML = 'Correo no válido'; + } else if (error.code === 'auth/user-not-found') { + document.querySelector('#errorLogin').innerHTML = 'Correo no registrado'; + } else if (error.code) { + document.getElementById('errorLogin').innerHTML = 'Ups algo no está bien'; + } + }); + authLogin(); + }); + + divHome.append( + btnGoogle, + btnLogin, + ); + + return divHome; +}; diff --git a/src/lib/Components/register.js b/src/lib/Components/register.js new file mode 100644 index 00000000..634e56e8 --- /dev/null +++ b/src/lib/Components/register.js @@ -0,0 +1,67 @@ +import { createUser, signInGoogle } from '../index.js'; +import { onNavigate } from '../../main.js'; + +export const register = () => { + const divRegister = document.createElement('div'); + divRegister.setAttribute('class', 'container-div-register'); + const viewRegister = ` + +
+

Crea tu cuenta

+ + + +
+
+
+ +
+ `; + divRegister.innerHTML = viewRegister; + const buttonFinalRegister = document.createElement('button'); + const buttonHome = document.createElement('button'); + + buttonFinalRegister.textContent = 'Crear cuenta'; + buttonFinalRegister.setAttribute('id', 'btnFinal'); + + buttonHome.textContent = 'Volver al inicio'; + buttonHome.setAttribute('id', 'btnReturn'); + buttonHome.addEventListener('click', () => onNavigate('/')); + + divRegister.appendChild(buttonHome); + divRegister.appendChild(buttonFinalRegister); + + const btnLoginGoogle = divRegister.querySelector('.btn-google'); + btnLoginGoogle.addEventListener('click', () => { + signInGoogle(onNavigate); + }); + + const btnFinal = divRegister.querySelector('#btnFinal'); + btnFinal.addEventListener('click', () => { + const userName = document.querySelector('#name-register').value; + const userEmail = document.querySelector('#email-register').value; + const userPassword = document.querySelector('#password-register').value; + + createUser(userEmail, userPassword, userName) + .then(() => { + onNavigate('/dashboard'); + }) + .catch((error) => { + if (error.code === 'auth/email-already-in-use') { + document.querySelector('#errorMessageRegister').innerHTML = 'Éste correo ya está registrado'; + } else if (error.code === 'auth/invalid-email') { + document.querySelector('#errorMessageRegister').innerHTML = 'Correo inválido'; + } else if (error.code === 'auth/invalid-password') { + document.querySelector('#errorMessageRegister').innerHTML = 'Tu clave tiene que tener un mínimo de seis dígitos'; + } + }); + }); + + divRegister.append( + btnFinal, + ); + + return divRegister; +}; diff --git a/src/lib/firebase.js b/src/lib/firebase.js new file mode 100644 index 00000000..c6bcf877 --- /dev/null +++ b/src/lib/firebase.js @@ -0,0 +1,15 @@ +import { initializeApp } from 'https://www.gstatic.com/firebasejs/9.15.0/firebase-app.js'; +import { getAuth } from 'https://www.gstatic.com/firebasejs/9.15.0/firebase-auth.js'; + +const firebaseConfig = { + apiKey: 'AIzaSyCLeEKdWYHeyxAAPoZ3-tpRs85uDEc9iYg', + authDomain: 'reading-club-43baf.firebaseapp.com', + projectId: 'reading-club-43baf', + storageBucket: 'reading-club-43baf.appspot.com', + messagingSenderId: '93329318817', + appId: '1:93329318817:web:b0b22eb377191fa03be1e0', +}; + +// Initialize Firebase +export const app = initializeApp(firebaseConfig); +export const auth = getAuth(app); diff --git a/src/lib/index.js b/src/lib/index.js index d1930899..c8431d2a 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -1,6 +1,137 @@ -// aqui exportaras las funciones que necesites +import { + getAuth, + GoogleAuthProvider, + createUserWithEmailAndPassword, + signInWithEmailAndPassword, + signInWithPopup, + signOut, + updateProfile, + onAuthStateChanged, +} from 'https://www.gstatic.com/firebasejs/9.15.0/firebase-auth.js'; +// } from "firebase/auth"; +import { + getFirestore, + collection, + addDoc, + getDocs, + query, + orderBy, + deleteDoc, + doc, + getDoc, + updateDoc, + arrayRemove, + arrayUnion, +} from 'https://www.gstatic.com/firebasejs/9.15.0/firebase-firestore.js'; +// } from "firebase/firestore"; + +import { onNavigate } from '../main.js'; +import { app } from './firebase.js'; + +// Initialize Firebase Authentication and get a reference to the service +export const auth = getAuth(app); + +export const authLogin = () => { + const userLogin = getAuth().onAuthStateChanged((user) => { + if (user) { + onNavigate('/dashboard'); + } + }); + return userLogin; +}; + +// Inicia el autentificador con google +export const provider = new GoogleAuthProvider(); + +// Initialize Firebase Firestore and get a reference to the service +export const firestore = getFirestore(); +export const postCollection = collection(firestore, 'post'); + +// CREAR USUARIO CON EMAIL +export const createUser = (userMail, userPass, userName) => createUserWithEmailAndPassword(auth, userMail, userPass) + .then(() => { + updateProfile(getAuth().currentUser, { + displayName: userName, + }); + }); + +// INGRESAR CON USUARIO EXISTENTE +export const signIn = (email, password) => signInWithEmailAndPassword(auth, email, password); + +// INGRESAR CON GOOGLE - check +export const signInGoogle = async (onNavigate) => { + try { + await signInWithPopup(auth, provider); + onNavigate('/dashboard'); + } catch (error) { + } +}; + +// Función checkout- check +export const logOut = async (onNavigate) => { + try { + await signOut(auth); + onNavigate('/'); + } catch (error) { + } +}; + +// funcion currentuser +export const currentUserInfo = () => auth.currentUser; + +// función publicar +export const submitPost = (postTxt) => { + const post = { + postText: postTxt, + user: getAuth().currentUser.displayName, + uid: getAuth().currentUser.uid, + createdDateTime: new Date(), + likes: [], + }; + return addDoc(postCollection, post); +}; + +// función para consultar todos los posts dispobibles en firestore +export const getAllPosts = async () => { + const querypost = query(postCollection, orderBy('createdDateTime', 'desc')); + const querySnapshot = await getDocs(querypost); + return querySnapshot; +}; + +// función para borrar post +export const deletePost = (id) => { + deleteDoc(doc(firestore, 'post', id)); +}; + +// funcion para traer un post para editar +export const getTask = (id) => getDoc(doc(firestore, 'post', id)); + +// función updateTask + +export const updateTask = (id, docData) => updateDoc(doc(firestore, 'post', id), { + postText: docData.postText, +}); + +// funcion like +export const giveLike = (id) => { + return updateDoc(doc(firestore, 'post', id), { + likes: arrayUnion(getAuth().currentUser.uid) + }); +} + +//funcion dislike +export const dislike = (id) => { + return updateDoc(doc(firestore,'post', id), { + likes: arrayRemove(getAuth().currentUser.uid) + }); +} + +export { + getAuth, + GoogleAuthProvider, + createUserWithEmailAndPassword, + signInWithEmailAndPassword, + signInWithPopup, + signOut, -export const myFunction = () => { - // aqui tu codigo - console.log('Hola mundo!'); }; diff --git a/src/main.js b/src/main.js index ac27e91a..aef046c6 100644 --- a/src/main.js +++ b/src/main.js @@ -1,5 +1,36 @@ -// Este es el punto de entrada de tu aplicacion +import { login } from './lib/Components/dashboard.js'; +import { register } from './lib/Components/register.js'; +import { home } from './lib/Components/home.js'; -import { myFunction } from './lib/index.js'; +const rootDiv = document.getElementById('root'); -myFunction(); +const routes = { + '/': home, + '/register': register, + '/dashboard': login, +}; + +export const onNavigate = (pathname) => { + window.history.pushState( + {}, + pathname, + window.location.origin + pathname, + ); + + while (rootDiv.firstChild) { + rootDiv.removeChild(rootDiv.firstChild); + } + + rootDiv.appendChild(routes[pathname]()); +}; + +rootDiv.appendChild(routes[window.location.pathname]()); + +const components = () => routes[window.location.pathname]; + +window.onpopstate = () => { + rootDiv.removeChild(rootDiv.firstChild); + rootDiv.append(components()); +}; + +rootDiv.appendChild(onNavigate()); diff --git a/src/style.css b/src/style.css new file mode 100644 index 00000000..95d2d2d7 --- /dev/null +++ b/src/style.css @@ -0,0 +1,616 @@ +* { + padding: 0; + margin: 0; + font-family: 'Montserrat', sans-serif; +} + +body { + background-color: #FCF8E8; +} + +html { + height: 100%; +} + +.div-home { + display: flex; + flex-direction: column; + align-items: center; +} + +.img-logo { + position: relative; + margin-top: 10%; + width: 200px; + height: 200px; + bottom: 30px; +} + +#img-google { + width: 130px; +} + +.btn-google { + display: flex; + position: relative; + border-color: white; + cursor: pointer; + margin-bottom: 100px; + bottom: 210px; + left: 2px; +} + +.root { + display: flex; + flex-wrap: wrap; + position: relative; +} + +.input-login, +.input-register { + display: flex; + position: relative; + flex-direction: column; + align-items: center; + background-color: #2D747F; + height: 350px; + width: 80%; + bottom: 60px; + +} + +.container-div-register { + display: flex; + flex-direction: column; + align-items: center; +} + +#btnRegister { + display: flexbox; + position: relative; + background-color: #9cd2dc; + cursor: pointer; + height: 25px; + width: 150px; + border-radius: 20px; + font-size: medium; + bottom: 125px; + +} + +#btnLogin { + display: flexbox; + position: relative; + bottom: 420px; + background-color: #ceb853; + cursor: pointer; + height: 25px; + width: 150px; + border-radius: 20px; + font-size: medium; +} + +#btnLogin:hover, +#btnRegister:hover, +.btn-google:hover, +#btn-sign-out:hover { + box-shadow: 2px 4px 4px 4px #6f7274; +} + +#formLogin { + align-items: center; +} + +#email, +#password { + margin-top: 5%; + border-radius: 25px; + border: solid 2px; + width: 230px; + height: 25px; + text-align: center; + font-size: medium; + position: relative; + outline: none; +} + +footer { + background-color: #2D747F; + ; + position: absolute; + width: 100%; + height: 2 rem; + text-align: center; + margin-top: 10px; + font-size: 12.5px; + font-weight: bold; + +} + +.input-login p { + margin-top: 5px; + font-size: 20px; + text-align: center; + font-weight: bold; + +} + +#errorLogin { + display: flexbox; + position: absolute; + top: 190px; + color: #602311; + left: 85px; + font-weight: bold; +} + +/*Registro*/ +#btnFinal { + position: relative; + background-color: #9cd2dc; + background-color: #9cd2dc; + cursor: pointer; + height: 25px; + width: 150px; + border-radius: 20px; + font-size: medium; + bottom: 380px; +} + +#btnReturn { + display: flexbox; + position: absolute; + top: 70%; + background-color: #ceb853; + cursor: pointer; + height: 25px; + width: 150px; + border-radius: 20px; + font-size: medium; + +} + +#name-register, +#email-register, +#password-register { + margin-top: 5%; + border-radius: 25px; + border: solid 2px; + width: 230px; + height: 25px; + text-align: center; + font-size: medium; + +} + +.input-register p { + margin-top: 5px; + font-size: 20px; + text-align: center; + font-weight: bold; +} + +#errorMessageRegister { + display: flexbox; + position: absolute; + top: 540px; + color: #602311; + font-weight: bold; + text-align: center; + font-size: 20px; + left: 45%; +} + +/*Dashboard*/ +.logo-header { + width: 150px; + height: 150px; + position: absolute; + top: -20px; +} + +#img-input { + margin-top: 8px; + width: 50px; + height: 50px; + margin-left: 10px; +} + +#img-modal-post { + margin-top: 5px; + width: 50px; + height: 50px; + margin-left: 6px; +} + +.btn-exit { + width: 25px; + height: 25px; + position: absolute; + left: 260px; + bottom: 200px; + cursor: pointer; +} + +header { + background-color: #2D747F; + height: 70px; +} + +#btn-sign-out { + background-color: transparent; + width: 30px; + height: 30px; + position: absolute; + top: 35px; + left: 85%; + border: transparent; +} + +#container-btn-input { + background-color: #2D747F; + width: 90%; + height: 85px; + margin: 20px 0px 20px 15px; + border-radius: 10px; + +} + +#btn-input-modal { + background-color: #DDD3A6; + height: 40px; + width: 65%; + align-items: center; + margin: 5px 10px 10px 10px; + position: relative; + bottom: 45px; + box-shadow: 1px 2px 4px rgb(155 154 154); + border-radius: 10px; + display: flex; + left: 80px; + cursor: pointer; +} + +#line { + border-top: 1px solid; +} + +#space-line { + background-color: #5FA0AA; + height: 25px; + text-align: center; + padding: 7px 7px 7px 7px; +} + +#after-line { + background-color: #9cd2dc; + display: flex; + flex-direction: column; +} + +#input-post { + height: 100px; + width: 90%; + background-color: white; + cursor: pointer; + border-color: grey; + border-radius: 10px; + margin-left: 3px; + padding: 3%; +} + +#modal-content-post { + width: 92%; + background-color: #dbefe2; + border-radius: 20px; + cursor: pointer; + display: none; + height: auto; + position: fixed; + top: 100px; + +} + +.btn-post-inactive { + background-color: #31E6F1; + width: 25%; + cursor: pointer; + border-radius: 10px; +} + +#modal-background-post { + height: 100vh; + width: 100%; + position: fixed; + z-index: 4; + background-color: rgba(133, 133, 133, 0.664); + display: none; + align-items: center; + justify-content: center; +} + +.timeLine-post { + background-color: #5FA0AA; + height: auto; + width: 80%; + border-radius: 10px; + margin-top: 30px; + left: 30px; + position: relative; + box-shadow: 1px 2px 4px rgb(155 154 154); + padding: 3%; + +} + +.iconUser { + position: absolute; + width: 50px; + height: 50px; +} + +.textPost { + position: relative; + display: grid; + background-color: #DDD3A6; + font-size: 15px; + width: 80%; + height: auto; + border-radius: 10px; + font-weight: light; + padding: 5%; + left: 20px; +} + +.user-name-post { + display: grid; + font-size: large; + justify-content: center; + padding: 5px; + color: white; + position: relative; +} + +#modal-content-post p { + font-size: large; + font-weight: bold; +} + +.date-post { + color: rgb(59, 59, 59); + position: relative; + font-size: 13px; + letter-spacing: 1px; +} + +.primer-like { + position: relative; + left: 10px; + width: 25px; + height: 25px; + top: 30px; + cursor: pointer; + +} + +.icon-edit { + position: relative; + left: 160px; + width: 20px; + height: 25px; + bottom: 35px; + cursor: pointer; +} + +.delete-icon { + position: relative; + width: 20px; + height: 20px; + bottom: 55px; + cursor: pointer; + left: 190px; +} + +.footInit { + background-color: #2D747F; + position: absolute; + width: 100%; + text-align: center; + margin-top: 10px; + font-size: 12.5px; + bottom: 0; + font-weight: bold; +} + +#container-post { + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-around; +} + +.sign-out-img { + width: 25px; + height: 25px; +} + +.like-number { + width: 25px; + height: 25px; + position: relative; + left: 45px; + bottom: 25px; + font-size: smaller; + top: 5px; +} + +@media only screen and (min-width: 600px) { + .timeLine-post { + left: 8%; + } +} + +@media only screen and (min-width: 1200px) { + .input-login { + width: 60%; + height: 370px; + } + + .input-register { + width: 60%; + height: 390px; + } + + .btn-google { + margin-top: 10px; + bottom: 210px; + } + + #btnLogin { + bottom: 300px; + } + + #btnFinal { + bottom: 0; + top: 70%; + + } + + #btnReturn { + bottom: 0; + top: 62%; + + } + +} + +@media only screen and (min-width: 1000px) { + .iconUser { + width: 50px; + height: 50px; + } + + .textPost { + left: 45px; + } + + #btn-input-modal { + width: 80%; + } + + .logo-header { + width: 250px; + height: 250px; + top: -50px; + } + + header { + height: 110px; + } + + .timeLine-post { + width: 60%; + left: 17.5%; + } + + .user-name-post { + font-size: large; + } + + #btn-sign-out { + left: 95%; + top: 65px; + } + + .delete-icon { + left: 95%; + } + + .icon-edit { + left: 90%; + } + + #container-btn-input { + width: 60%; + } + + footer { + font-size: 14px; + } + + .btn-exit { + left: 95%; + bottom: 90%; + } + + #modal-content-post { + width: 50%; + } + + .img-logo { + width: 300px; + height: 300px; + margin-top: 0; + } + + #email, + #password { + margin-top: 3%; + width: 410px; + height: 45px; + } + + #btnLogin { + height: 35px; + width: 250px; + font-size: 28px; + } + + #btnRegister { + height: 35px; + width: 250px; + font-size: 28px; + } + + .input-login, + .input-register { + bottom: 77px; + } + + .btn-google { + margin-bottom: 0; + } + + .input-login p { + font-size: 28px; + } + + #name-register, + #email-register, + #password-register { + margin-top: 2%; + width: 400px; + height: 40px; + } + + #btnFinal { + position: relative; + margin-left: 0; + height: 35px; + width: 250px; + font-size: 28px; + top: -170px; + } + + #btnReturn { + bottom: 70%; + height: 35px; + width: 250px; + font-size: 28px; + + } + + #input-post { + left: 20px; + } + +} \ No newline at end of file diff --git a/test/auth.spec.js b/test/auth.spec.js new file mode 100644 index 00000000..fbaefa69 --- /dev/null +++ b/test/auth.spec.js @@ -0,0 +1,69 @@ +// importamos la funcion que vamos a testear +import { + getAuth, + GoogleAuthProvider, + createUserWithEmailAndPassword, + signInWithEmailAndPassword, + signInWithPopup, + signOut, + auth, + signInGoogle, + logOut, + currentUserInfo, + signIn, + createUser, +} from '../src/lib/index.js'; + +jest.mock('../src/lib/index.js', () => ({ + auth: jest.fn(() => ({ auth: 'test' })), + createUserWithEmailAndPassword: jest.fn ((auth, userMail, UserPass) => { + if(!userMail || !UserPass){ + throw new Error('Error'); + } + Promise.resolve({ + email: 'sus' + }); + }), + + signInWithEmailAndPassword: jest.fn(), + signOut: jest.fn(), + GoogleAuthProvider: jest.fn(), + getAuth: jest.fn(), + signInWithPopup: jest.fn(), + currentUserInfo: jest.fn(), + +})); + +describe('Test para createUser', () => { + const userName = 'Laura'; + const userMail = 'petsbook@gmail.com'; + const userPass = '1234567'; + it('Deberia llamar a createUserWithEmailAndPassword', () => { + createUser(userMail, userPass, userName); + expect(createUserWithEmailAndPassword).toHaveBeenCalled(); + }); + it('Debería llamar a createUserWithEmailAndPassword con sus parametros', () => { + createUser(userMail, userPass, userName); + expect(createUserWithEmailAndPassword).toHaveBeenCalledWith(auth, userMail, userPass) +}); +it('signup debería ser una funcion', () => { + expect(typeof createUser).toBe('function') +}); +it('Debería recibir el email', () => { + createUser(userMail, userPass, userName); + expect(createUserWithEmailAndPassword.mock.calls[0][1]).toBe('readingbook@gmail.com') +}); +it('Debería recibir el password', () => { + createUser(userMail, userPass, userName); + expect(createUserWithEmailAndPassword.mock.calls[0][2]).toBe('1234567') +}); +it('Debería recibir el nombre', () => { + createUser(userMail, userPass, userName); + expect(createUserWithEmailAndPassword.mock.calls[0][3]).toBe('Laura') +}) +}); + + + + + diff --git a/test/firestore.spec.js b/test/firestore.spec.js new file mode 100644 index 00000000..e69de29b diff --git a/test/index.spec.js b/test/index.spec.js deleted file mode 100644 index 91f11a33..00000000 --- a/test/index.spec.js +++ /dev/null @@ -1,8 +0,0 @@ -// importamos la funcion que vamos a testear -import { myFunction } from '../src/lib/index'; - -describe('myFunction', () => { - it('debería ser una función', () => { - expect(typeof myFunction).toBe('function'); - }); -});