diff --git a/.gitignore b/.gitignore index 80e70b86..ac635819 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ coverage/ node_modules/ .vscode/ -.env \ No newline at end of file +*.env +config.js +package-lock.json \ No newline at end of file diff --git a/README.md b/README.md index 4d88151c..d7f21b5b 100644 --- a/README.md +++ b/README.md @@ -1,811 +1,47 @@ -# Dataverse Chat - -## Índice - -* [1. Preámbulo](#1-preámbulo) -* [2. Resumen del proyecto](#2-resumen-del-proyecto) -* [3. Consideraciones generales](#3-consideraciones-generales) -* [4. Funcionalidades](#4-funcionalidades) -* [5. Consideraciones técnicas](#5-consideraciones-técnicas) -* [6. Hitos](#6-hitos) -* [7. Criterios de aceptación mínimos del proyecto](#7-criterios-de-aceptación-mínimos-del-proyecto) -* [8. Objetivos de aprendizaje](#8-objetivos-de-aprendizaje) -* [9. Pistas, tips y lecturas complementarias](#9-pistas-tips-y-lecturas-complementarias) -* [10. Consideraciones para pedir tu Project Feedback](#10-consideraciones-para-pedir-tu-project-feedback) - -*** - -## 1. Preámbulo - -En nuestro día a día, las aplicaciones web son como amigas -inseparables. Desde las redes sociales hasta las herramientas de -trabajo, las usamos todo el tiempo. Pero lo que hace que este -momento sea realmente especial es que estas aplicaciones nos pueden -abrir una puerta a algo asombroso: la interacción con inteligencia -artificial. - -¿Qué tal si pudiéramos conversar con alguien que vivió hace más de -cien años o incluso con los personajes de nuestras series y -películas favoritas o con entidades que normalmente no pueden hablar -como por ejemplo planetas o animales? La inteligencia artificial nos -puede ayudar a entender el pasado y a conectarnos con figuras -históricas, así como a sumergirnos en mundos ficticios. - -![Preview app](https://github-production-user-asset-6210df.s3.amazonaws.com/123121338/271433237-2bd1477b-15ef-49d4-9fcb-226b3263c46a.png) - -## 2. Resumen del proyecto - -En este proyecto convertirás la aplicación desarrollada -en Dataverse en una -[Single Page Application (SPA)](https://es.wikipedia.org/wiki/Single-page_application), -manteniendo las funcionalidades de visualizar, filtrar, ordenar y -calcular alguna estadística, adicionando una nueva vista para -consultar información detallada de cada personaje/entidad y agregando -la posibilidad de interactuar con un personaje/entidad o todos ellos -a través de un sistema de chat impulsado por la -[API de OpenAI](https://openai.com/product). - -### Los objetivos generales de este proyecto son los siguientes - -* Desarrollar una [Single Page Application (SPA)](https://es.wikipedia.org/wiki/Single-page_application) -* Aplicar los conceptos de responsividad en el desarrollo de las vistas -* Implementar un router para la navegación entre las diferentes -vistas de la aplicación -* Integrar una API externa -* Entender la asincronía en JavaScript -* Crear una suite de pruebas unitarias que permitan testear código asíncrono - -## 3. Consideraciones generales - -* Este proyecto se debe resolver en duplas. - -* El rango de tiempo estimado para completar el proyecto es de 4 a 5 Sprints. - -* La lógica del proyecto debe estar implementada completamente en JavaScript - (ES6+), HTML y CSS. Para este proyecto **no está permitido** utilizar - _frameworks_ o librerías de CSS y JS. - -* Reutilizar cierta parte del código del proyecto Dataverse, sobre todo el dataset. - Además debes refactorizar el código para que sea más modular y reutilizable - implementando nuevas funciones para lograr las funcionalidades requeridas - en este proyecto. - -* El proyecto será entregado subiendo tu código a GitHub (commit/push) y la - interfaz será desplegada usando cualquiera de estas opciones: - [Vercel](https://vercel.com/) o [Netlify](https://www.netlify.com/). - -* 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. Aprovecha 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. - Para comenzar tendrás que hacer un _fork_ y _clonar_ este repositorio. - -## 4. Funcionalidades - -Como entregable final tendrás una Single Page Application (SPA) que permita, -además de **visualizar la data, filtrarla, ordenarla y calcular alguna -estadística** tal como se hizo en Dataverse, acceder a una página de detalle -de cada personaje y poder _interactuar_ con -los personajes o entidades del set de data que utilizaste anteriormente. - -Aquí definimos en más detalle las funcionalidades mínimas que debe tener: - -* La aplicación debe ser _responsive_ -* La aplicación debe ser una SPA con múltiples vistas: - - Implementar un sistema de enrutamiento que permita la navegación - dentro de la aplicación. - - Cada vista de la aplicación debe ser cargada dinámicamente - mediante JavaScript. - - Asegurarse de que la URL se actualice de manera acorde a la vista - cargada al igual que el `title` del documento (la pestaña del navegador). - - La aplicación debe ser capaz de cargar la vista correspondiente a - la URL actual al iniciar la aplicación. -* La aplicación debe mantener las funcionalidades de Dataverse: visualizar, - filtrar, ordenar y calcular estadística de la data. -* Al hacer clic en una tarjeta de personaje/entidad, la aplicación debe - redirigirse a una vista **con su propia URL** que muestre la información - detallada sobre ese personaje/entidad en particular -* La aplicación debe permitir a la usuaria configurar la API Key para - interactuar con la API de Open AI -* Usando la API de Open AI, la aplicación debe permitir al usuario - interactuar con un personaje/entidad a través de un chat. - Por ejemplo, si usamos la data de ejemplo de Dataverse, correspondiente - a las mujeres con mayores contribuciones a la programación en la historia, - una usuaria puede establecer una conversación - interactiva con - [Ada Lovelace](https://es.wikipedia.org/wiki/Ada_Lovelace) - a través del sistema de chat, obteniendo - información sobre sus logros, desafíos y contribuciones a la informática. - La inteligencia artificial de OpenAI permite que las respuestas sean - informativas y personalizadas según las preguntas de los usuarios. - -```text -Usuaria: "Hola, Ada. Cuéntame más sobre tus contribuciones a la informática." - -Ada Lovelace (AI de OpenAI): "¡Hola! Claro, estaré encantada de hablar sobre eso. -Mis contribuciones más destacadas se relacionan con el trabajo junto a Charles Babbage -en el diseño de la Máquina Analítica. Además, desarrollé el primer algoritmo destinado -a ser procesado por una máquina, lo que me convierte en la primera programadora de la historia. -¿Hay algo más en particular que te gustaría saber?" - -Usuaria: "¡Eso es fascinante! ¿Qué desafíos enfrentaste en ese tiempo?" - -Ada Lovelace (AI de OpenAI): "En ese tiempo, enfrenté varios desafíos, uno de los -principales fue la limitación de recursos y la falta de reconocimiento del potencial -de la Máquina Analítica. También tuve que superar las restricciones sociales de -la época, ya que las mujeres tenían un acceso limitado a la educación en -matemáticas y ciencias. Sin embargo, mi pasión por la matemática y la visión -de lo que estas máquinas podrían lograr me impulsaron a seguir adelante." -``` - -* La aplicación debe permitir al usuario interactuar de manera simultánea - con **todos** los personajes/entidades a través de un chat: - - Esta funcionalidad debe cargarse en la URL `/panel` - - La usuaria puede ingresar su pregunta o mensaje para todos los - personajes/entidades en un cuadro de texto y enviarlo con un botón - - El mensaje de la usuaria debe ser ajustado para cada personaje/entidad, - con el objetivo que este genere una respuesta basada en su personalidad - y conocimiento - - Las respuestas de todos los personajes se muestran de acuerdo al orden - respuesta. - - Indicar visualmente cuando uno o varios personajes/entidades esten - generando una respuesta al mensaje enviado -* La aplicación debe informar a la usuaria los errores que puedan surgir al - interactuar con la API, como por ejemplo alcanzar la cuota de tokens por - minuto o cualquier otro error relacionado con la API. Debería - proporcionarse una descripción clara de la causa del problema y posibles - soluciones. - -Para que los chats anteriores puedan funcionar, es esencial que la aplicación -se integre con la IA a través de la API de OpenAI. Para ello, el equipo de -Laboratoria te proporcionará una API Key que la usuaria deberá poder ingresar -en la aplicación que tu construirás. - -_Nota_: puedes revisar esta [implementación](https://laboratoria-dataverse-talks.netlify.app/) -de ejemplo, cuyo principal objetivo es mostrarte cómo se vería la -funcionalidad de chat en tu aplicación, más no tomes el diseño como -referencia para tu proyecto, siente libre de hacer volar tu imaginación -diseñando tu propia interfaz. - -## 5. Consideraciones técnicas - -La lógica del proyecto debe estar implementada completamente en JavaScript -(ES6), HTML y CSS. En este proyecto NO está permitido usar librerías o -frameworks, solo [vanilla JavaScript](https://medium.com/laboratoria-how-to/vanillajs-vs-jquery-31e623bbd46e). - -El _boilerplate_ que viene con el proyecto es una estructura inicial. Ustedes -deben ir agregando más archivos conforme vayan avanzando en su proyecto. -La siguiente es una estructura de archivos que recomendamos: - -```text -. -├── src -| ├── components -| ├── data -| | └── dataset.js -| ├── lib -| | └── dataFunctions.js -| ├── views -| ├── index.html -| ├── index.js -| ├── router.js -| └── style.css -├── test -| └── dataFunctions.spec.js -| └── example.spec.js -├── README.md -└── package.json - -``` - -### `src/components` - -Esta carpeta contendrá archivos JavaScript -que representan [componentes](https://lenguajejs.com/vuejs/componentes/que-es-un-componente/) -reutilizables de tu aplicación. Estos componentes -pueden ser elementos de la interfaz de usuario que se utilizan en varias partes -de tu aplicación. Organizar tus componentes de esta manera ayuda a mantener un -código más modular y reutilizable. - -### `src/data` - -En esta carpeta están los datos con los que vas a trabajar (los datos de ejemplo -o los datos que generarías con ayuda de la inteligencia artificial). - -### `src/lib` - -En esta carpeta, crearás módulos que contengan funcionalidades no relacionadas -al DOM. Aquí podrás incluir el archivo -`dataFunctions.js` que contiene las funciones que te permiten filtrar, -ordenar y hacer cálculos agregados. También podrás crear archivos con -las funciones que -interactúen con la API de inteligencia artificial o utilidades destinadas -a almacenar datos en el local storage. - -### `src/views` - -Esta carpeta generalmente contiene archivos de los componentes que -representan las diferentes páginas o vistas de tu aplicación. -Cada vista (view) puede tener su propio archivo -JavaScript, que exporta una función que es responsable de generar -los elementos del DOM. - -### `src/index.html` - -Como en el proyecto anterior, existe un archivo `index.html`. En este archivo -HTML encontrarás un elemento DOM `
` donde vas a -renderizar las diferentes vistas de tu aplicación. - -### `src/index.js` - -El punto de partida para tu SPA sera el archivo `src/index.js`. Acá encontrarás -algunas instrucciones y comentarios para guiarte en el inicio del desarrollo -de tu aplicación. - -### `src/router.js` - -En este archivo vas a manejar el enrutamiento de tu aplicación. Es un archivo -que debes crearlo por tu cuenta. - -### `src/style.css` - -Es un archivo que debes crearlo por tu cuenta para agregar los estilos que -necesites para tu aplicación. - -### `src/test` - -Esta carpeta contiene los tests de tus funciones y componentes. Por ejemplo -los tests de dataFunctions deben estar aquí. - -## 6. Hitos - -Para resolver un problema, -es importante comprenderlo en profundidad. -Una forma de hacerlo es descomponerlo en problemas más pequeños. -Esto nos ayudará a identificar las causas del problema y -a desarrollar soluciones más efectivas. - -En este proyecto, te sugerimos que trabajes por hitos. -Esto te permitirá enfocarte en un problema a la vez y -hacer un seguimiento de tu progreso. -A continuación, te presentamos un cronograma de hitos que -te puede ayudar a organizar tu trabajo. - -* [Hito 1](./docs/01-milestone.md) -* [Hito 2](./docs/02-milestone.md) -* [Hito 3](./docs/03-milestone.md) -* [Hito 4](./docs/04-milestone.md) -* [Hito 5](./docs/05-milestone.md) - -## 7. Criterios de aceptación mínimos del proyecto - -Además de las funcionalidades que definimos en la sección de -[Funcionalidades](#4-funcionalidades), tu proyecto debe cumplir con los -siguientes requisitos: - -### Definición del producto - -Documenta brevemente tu trabajo en el archivo `README.md` de tu repositorio, -contándonos cómo fue tu proceso de diseño y cómo crees que el producto resuelve -el problema (o problemas) que tiene tu usuaria. - -### Historias de usuaria - -Una vez que entiendas las necesidades de tus usuarias, escribe las [Historias -de Usuaria](https://es.wikipedia.org/wiki/Historias_de_usuario) que representen -todo lo que la usuaria necesita hacer/ver. Las **Historias de Usuaria** deben -ser el resultado de tu proceso de investigación o _research_ de tus usuarias. - -Asegúrate de incluir la definición de terminado (_definition of done_) y los -Criterios de Aceptación para cada una. - -Usa tus historias de usuaria para planificar tus sprints dividiendo -cada historia en tareas. - -En la medida de lo posible, termina una Historia de Usuaria antes de pasar -a la siguiente (cumpliendo con la Definición de Terminado y los Criterios -de Aceptación). - -### Diseño de la Interfaz de Usuaria - -#### Prototipo de baja fidelidad - -Durante tu trabajo deberás haber hecho e iterado bocetos (_sketches_) de tu -solución usando papel y lápiz. Te recomendamos tomar fotos de todas las -iteraciones que hagas, que las subas a tu repositorio y las menciones en tu -`README.md`. - -Recuerda pedir feedback de tu prototipo a tus compañeras y/o coaches. - -#### Prototipo de alta fidelidad - -Lo siguiente es diseñar tu Interfaz de Usuaria (UI por sus siglas en inglés - -_User Interface_). Para eso debes aprender a utilizar alguna herramienta de -diseño visual. Nosotros te recomendamos [Figma](https://www.figma.com/) que es -una herramienta que funciona en el navegador y, además, puedes crear una cuenta -gratis. Sin embargo, eres libre de utilizar otros editores gráficos como -Illustrator, Photoshop, etc. - -El diseño debe representar el _ideal_ de tu solución. Digamos que es lo que -desearías implementar si tuvieras tiempo ilimitado para trabajar. Además, tu -diseño debe seguir los fundamentos de _visual design_. - -Recuerda pedir feedback de tu prototipo a tus compañeras y/o coaches. - -### Testeos de usabilidad - -Durante el reto deberás hacer _tests_ de usabilidad con distintos usuarias, -y con base en los resultados, deberás iterar tus diseños. Cuéntanos -qué problemas de usabilidad detectaste a través de los _tests_ y cómo los -mejoraste en tu propuesta final. - -### Implementación de la Interfaz de Usuaria (HTML/CSS/JS) - -Luego de diseñar tu interfaz de usuaria deberás trabajar en su implementación. -**No** es necesario que construyas la interfaz exactamente como la diseñaste. -Tu tiempo de hacking es escaso, así que deberás priorizar. - -Revisa [las funcionalidades](#4-funcionalidades) que el proyecto pide del interfaz. - -### Pruebas unitarias - -El _boilerplate_ de este proyecto no incluye Pruebas Unitarias (_tests_), así es -que tendrás que escribirlas tú para las funciones encargadas de _procesar_, -_filtrar_ y _ordenar_ la data, así como _calcular_ estadísticas. -Te recomendamos usar -el framework [Jest](https://jestjs.io/) para ejecutar las pruebas unitarias. -Consultar su documentación. - -Tus _pruebas unitarias_ deben dar una cobertura del 70% de _statements_ -(_sentencias_), _functions_ (_funciones_), _lines_ (_líneas_), y _branches_ -(_ramas_) del archivo `src/dataFunctions.js` que contenga tus funciones y -está detallado en la sección de [Consideraciones técnicas](#5-consideraciones-técnicas). - -## 8. 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/html5/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/css/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/css/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 - -- [ ] **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) -

- -- [ ] **Browser storage (localStorage, sessionStorage)** - -
Links

- - * [Window.localStorage - MDN](https://developer.mozilla.org/es/docs/Web/API/Window/localStorage) -

- -- [ ] **Fetch API** - -
Links

- - * [Fetch API - MDN](https://developer.mozilla.org/es/docs/Web/API/Fetch_API) -

- -#### DOM (Document Object Model) - -- [ ] **Uso de selectores del DOM** - -
Links

- - * [Manipulación del DOM](https://curriculum.laboratoria.la/es/topics/browser/dom/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) -

- -### JavaScript - -- [ ] **Uso de identificadores descriptivos (Nomenclatura y Semántica)** - -- [ ] **Variables (declaración, asignación, ámbito)** - -
Links

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

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

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

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

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

- -- [ ] **Diferenciar entre expresiones (expressions) y sentencias (statements)** - -- [ ] **Uso de bucles/ciclos (while, for, for..of)** - -
Links

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

- -#### Asincronía - -- [ ] **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/) -

- -#### Tipos de datos - -- [ ] **Diferenciar entre tipos de datos primitivos y no primitivos** - -- [ ] **Arrays (arreglos)** - -
Links

- - * [Arreglos](https://curriculum.laboratoria.la/es/topics/javascript/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/objects/objects) -

- -#### Módulos - -- [ ] **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) -

- -#### Testing en Javascript - -- [ ] **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) -

- -### HTTP - -- [ ] **Cabeceras (headers)** - -
Links

- - * [HTTP headers - MDN](https://developer.mozilla.org/es/docs/Web/HTTP/Headers) -

- -- [ ] **Consulta o petición (request) y respuesta (response).** - -
Links

- - * [Generalidades del protocolo HTTP - MDN](https://developer.mozilla.org/es/docs/Web/HTTP/Overview) - * [Mensajes HTTP - MDN](https://developer.mozilla.org/es/docs/Web/HTTP/Messages) -

- -- [ ] **Códigos de status de HTTP** - -
Links

- - * [Códigos de estado de respuesta HTTP - MDN](https://developer.mozilla.org/es/docs/Web/HTTP/Status) - * [The Complete Guide to Status Codes for Meaningful ReST APIs - dev.to](https://dev.to/khaosdoctor/the-complete-guide-to-status-codes-for-meaningful-rest-apis-1-5c5) -

- -### AI Prompting - -- [ ] **Priming Chatbots** - -
Links

- - * [Priming Chatbots | Learn Prompting: Your Guide to Communicating with AI](https://learnprompting.org/es/docs/basics/priming_prompt) -

- -- [ ] **OpenAI API** - -
Links

- - * [Introduction - API Reference - OpenAI API](https://platform.openai.com/docs/introduction) - * [Authentication - API Reference - OpenAI API](https://platform.openai.com/docs/api-reference/authentication) - * [Making requests - API Reference - OpenAI API](https://platform.openai.com/docs/api-reference/making-requests) - * [The chat completion object - API Reference - OpenAI API](https://platform.openai.com/docs/api-reference/chat/object) -

- -### Control de Versiones (Git y GitHub) - -#### Git - -- [ ] **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 - -- [ ] **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)** - -### 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) -

- -## 9. Pistas, tips y lecturas complementarias - -### Primeros pasos - -Súmate al canal de Slack -[#project-dataverse-chat](https://claseslaboratoria.slack.com/archives/C03MV35EP5M) -para conversar y pedir ayuda del proyecto. - -Te sugerimos algunas tareas para dar inicio al proyecto. No es necesario -abordarlas de manera secuencial, y puedes llevarlas a cabo en paralelo -mientras avanzas con la planificación de las Historias de Usuario: - -#### Diseño de Wireframes o Prototipo de Baja Fidelidad - -Comienza por diseñar wireframes o prototipos de baja fidelidad para capturar -los requisitos funcionales de tu proyecto. Es recomendable comenzar con un -enfoque ["mobile first"](https://mediaclick.es/blog/diseno-web-responsive-design-y-la-importancia-del-mobile-first/) -para garantizar la usabilidad en dispositivos móviles, -y luego extender la atención a pantallas más grandes, como las de escritorio. - -#### Comprende los Conceptos de SPA (Single Page Application) y Enrutamiento - -Antes de comenzar a codificar, asegúrate de comprender los conceptos de una -Aplicación de Página Única (SPA) y el enrutamiento de páginas. Esto te -permitirá crear una experiencia de usuario fluida y dinámica. Investiga -las mejores prácticas y las herramientas disponibles para implementar un -SPA en tu proyecto. - -#### Preparación de la Estructura del Proyecto - -Organiza tu proyecto creando la estructura de directorios necesaria y -configurando un sistema de enrutamiento básico para cambiar dinámicamente -entre dos vistas. Asegúrate de que tu proyecto tenga una arquitectura -limpia y escalable. - -#### Integración de Dataverse - -Si tienes la intención de utilizar código de Dataverse en tu proyecto, -asegúrate de importar los archivos relevantes, como `dataset.js`, -`dataFunctions.js`, `main.js`, y `view.js`, junto con sus respectivas -pruebas. Ten en cuenta que `main.js` y `view.js` requerirán una -refactorización para adaptarse a las vistas y componentes específicos -de tu proyecto. - -#### Identifica las funcionalides de Open AI - -Consulta la documentación de la -[API de Open AI](https://platform.openai.com/docs/guides/gpt?lang=node.js). -Identifica y prueba las funciones que usarás para construir el chat -de tu aplicación - -#### Cuando ya estés lista para codear, te sugerimos empezar de esta manera - -1. Una de las integrantes del equipo debe realizar un :fork_and_knife: - [fork](https://help.github.com/articles/fork-a-repo/) del repo de tu cohort, - tus _coaches_ te compartirán un _link_ a un repo y te darán acceso de lectura - en ese repo. La otra integrante del equipo deber hacer un fork **del - repositorio de su compañera** y - [configurar](https://gist.github.com/BCasal/026e4c7f5c71418485c1) un `remote` - hacia el mismo. -2. :arrow_down: [Clona](https://help.github.com/articles/cloning-a-repository/) - tu _fork_ a tu computadora (copia local). -3. 📦 Instala las dependencias del proyecto con el comando `npm install`. Esto - asume que has instalado [Node.js](https://nodejs.org/) (que incluye [npm](https://docs.npmjs.com/)). -4. Si todo ha ido bien, deberías poder ejecutar las :traffic_light: - pruebas unitarias (unit tests) con el comando `npm test`. -5. Para ver la interfaz de tu programa en el navegador, usa el comando - `npm start` para arrancar el servidor web y dirígete a - `http://localhost:5000` en tu navegador. -6. A codear se ha dicho! :rocket: - -### Contenido de referencia - -#### Desarrollo Front-end - -* [Topicos en la curricula de sitio Laboratoria](https://curriculum.laboratoria.la/es/web-dev/topics) - testing, arreglos, objetos, funciones, DOM en Browser Javascript. -* [Promesas](https://es.javascript.info/promise-basics) -* [Haciendo peticiones con Fetch](https://developer.mozilla.org/es/docs/Web/API/Fetch_API/Utilizando_Fetch) -* [Almacenamiento con LocalStorage](https://es.javascript.info/localstorage) -* [Creando un router SPA con vanilla Javascript](https://github.com/Laboratoria/curriculum/blob/main/guides/router-spa/README.md) -* [Delegación de eventos](https://es.javascript.info/event-delegation) - -#### Inteligencia Artificial y Prompting - -* [Learn Prompting](https://learnprompting.org/es/docs/category/-basic-applications) -* [Prompt Engineering Roadmap](https://roadmap.sh/prompt-engineering) -* [OpenAI Playground](https://beta.openai.com/playground) -* [Making requests](https://platform.openai.com/docs/api-reference/making-requests) -* [Chat with AI](https://platform.openai.com/docs/api-reference/chat) -* [Priming Chatbots](https://learnprompting.org/es/docs/basics/priming_prompt) -* [Build your aplication with AI](https://platform.openai.com/docs/quickstart/build-your-application) - -#### Herramientas - -* [Jest](https://jestjs.io/) -* [Vercel](https://vercel.com/) -* [Netlify](https://www.netlify.com/) - -## 10. Consideraciones para pedir tu Project Feedback - -Antes de agendar tu Project Feedback con un coach, asegúrate que tu proyecto: - -* [ ] Cumple con todas las pruebas unitarias al ejecutar `npm run test` y - que tienen una cobertura del 70% de _statements_ (_sentencias_), - _functions_ (_funciones_), _lines_ (_líneas_), y _branches_ -* [ ] Esta libre de _errores_ de `eslint` al ejecutar `npm run pretest` -* [ ] Está subido a GitHub y desplegado en Netlify o Vercel. -* Tiene un `README.md` con la siguiente: - - [ ] _Definición del producto_ clara e informativa - - [ ] Historias de usuaria - - [ ] Un _sketch_ de la solución (prototipo de baja fidelidad) y - _Diseño de la Interfaz de Usuaria_ (prototipo de alta fidelidad) - - [ ] El listado de problemas que detectaste a través de tests - de usabilidad en el `README.md` -* Tiene un UI que cumple las funcionalidades: - - [ ] Permite ver toda la data en la pantalla principal con opciones para - filtrar y ordenar. - - [ ] Permite ingresar un API Key a través de un formulario. - - [ ] Permite ver una página con más detalles de cada entidad. En esta página - se puede iniciar el chat. - - [ ] Permite conversar con el personaje o entidad de la data a través - de un chat. - - [ ] Permite chatear de forma grupal con todas las entidades de la data. - - [ ] Es responsive y fue realizado usando la metodología mobile first. - -Recuerda que debes hacer una autoevaluación de _objetivos de aprendizaje_ y -_life skills_ desde tu dashboard de estudiante. - -Si no has completado todo lo anterior, no consideramos que estás lista para -tu sesión de Project Feedback. +## Resumen del Proyecto + +Este proyecto convierte una aplicación desarrollada en Dataverse en una Single Page Application (SPA), añadiendo funcionalidades como una vista detallada para cada personaje y un sistema de chat interactivo utilizando la API de OpenAI. + +![Preview app](src/imagenes/Captura de Pantalla 2024-08-25 a la(s) 18.51.19.png) + +## Objetivos del Proyecto +Desarrollar una SPA con navegación fluida. +Aplicar conceptos de diseño responsivo. +Implementar un router para la navegación entre vistas. +Integrar la API de OpenAI para chat interactivo. +Manejar la asincronía en JavaScript y crear pruebas unitarias para código asíncrono. +# Estructura del Proyecto +* src/components: Componentes reutilizables de la interfaz de usuario. +* src/data: Datos de ejemplo o generados por IA. +* src/lib: Funcionalidades no relacionadas al DOM, como filtros, ordenaciones y funciones para la API de IA. +* src/views: Archivos para las diferentes vistas de la aplicación. +* src/index.html: Archivo HTML principal con
. +* src/index.js: Punto de partida para la SPA. +* src/router.js: Archivo para manejar el enrutamiento (deberás crearlo). +* src/style.css: Archivo para estilos personalizados (deberás crearlo). +* src/test: Tests para funciones y componentes, incluyendo dataFunctions. +## Criterios de Aceptación Mínimos +* Definición del Producto: Documenta el proceso de diseño y cómo el producto resuelve los problemas de las usuarias en el README.md. +* Historias de Usuaria: Crea Historias de Usuaria que representen las necesidades de las usuarias, incluyendo definición de terminado y Criterios de Aceptación para cada historia. +## Diseño de la Interfaz de Usuaria: +![Preview app](imagenes/src/imagenes/Captura de Pantalla 2024-08-25 a la(s) 19.03.12.png) + +* * Prototipo de Baja Fidelidad: Bocetos iniciales, subidos al repositorio y mencionados en el README.md. +* * Prototipo de Alta Fidelidad: Diseño visual en herramientas como Figma, representando el ideal de la solución. +* * Testeos de Usabilidad: Realiza pruebas de usabilidad y documenta problemas detectados y mejoras realizadas. +* * Implementación de la Interfaz de Usuaria: Implementa la interfaz en HTML/CSS/JS, priorizando las funcionalidades clave. +* * Pruebas Unitarias: Escribe pruebas unitarias con cobertura del 70% para funciones de procesamiento de datos usando Jest. +## Objetivos de Aprendizaje +* HTML: Uso de HTML semántico. +* CSS: Uso de selectores, modelo de caja, flexbox, y CSS Grid Layout. +* Web APIs: Ruteado, almacenamiento en navegador, Fetch API. +* DOM: Selectores, manejo de eventos, manipulación dinámica. +* JavaScript: Identificadores descriptivos, variables, funciones, condicionales, bucles, asincronía (callbacks, promesas), tipos de datos, módulos ES6. +* Testing en JavaScript: Pruebas unitarias y asíncronas, uso de mocks y espías. +* HTTP: Cabeceras, peticiones y respuestas, códigos de status. +* AI Prompting: OpenAI API. +* Control de Versiones (Git y GitHub): Instalación, configuración, control de versiones, integración de cambios, despliegue con GitHub Pages. +* Centrado en el Usuario: Diseño centrado en el usuario y prototipos de alta fidelidad. +* Investigación: Testeos de usabilidad de prototipos en distintos niveles de fidelidad. + +Despliegue del proyecto por netlify : https://main--dataverse-chat.netlify.app/ diff --git a/package.json b/package.json index 25f2001e..9a5514ed 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "babel-jest": "^27.0.1", "css": "^3.0.0", "eslint": "^8.3.0", + "eslint-plugin-import": "^2.29.1", "gh-pages": "^3.1.0", "htmlhint": "^1.0.0", "jest": "^27.0.1", @@ -35,5 +36,10 @@ "createdAt": "2024-07-01T22:30:34.915Z", "version": "9.1.1", "commit": "1843c213affffa12aa456fa8842b6d28f97de2d3" + }, + "dependencies": { + "axios": "^1.7.4", + "dotenv": "^16.4.5", + "openai": "^4.56.0" } -} \ No newline at end of file +} diff --git a/src/card.js b/src/card.js new file mode 100644 index 00000000..3a0a821e --- /dev/null +++ b/src/card.js @@ -0,0 +1,30 @@ +import { navigateTo } from '../../router.js'; + +export const renderCard = (card, full_data = false) => { //creando la constante para importarla en view.js + + const ul = document.createElement("ul"); //creando el ul + ul.classList.add("card"); //agregando la clase card + ul.setAttribute("itemscope", ""); // Indicando que es un contenedor de datos + ul.setAttribute("itemtype", "https://schema.org/Person"); // Especificando el tipo de datos + + ul.addEventListener('click', function () { + navigateTo('/detail', { id: card['id'] }); + }); + + // revisamos si tiene fecha de la muerte. porque no todos tienen este dato + const yearOfDeath = card['facts']['yearOfDeath'] ? card['facts']['yearOfDeath'] : '-'; + let datos = ` +
  • Imagen de ${card['name']}
  • +
  • Nombre: ${card['name']}
  • `; //creando tarjeta que contiene informacion, debe ser trabajada en css + if (full_data) { + datos = datos + `
  • Descripción: ${card['shortDescription']}
  • +
  • Año de Nacimiento: ${card['facts']['yearOfBirth']}
  • +
  • Fecha de Muerte: ${yearOfDeath}
  • +
  • Lugar de Nacimiento: ${card['facts']['birthPlace']}
  • +
  • Especialidad: ${card['facts']['mainField']}
  • + `; // si se necesitan todos los datos, se agregan si se pasa el parametro full como true + } + + ul.innerHTML = datos; //indicandole al ul que contendra el bloque html de la tarjeta + return ul; //retornando div +} \ No newline at end of file diff --git a/src/categories.js b/src/categories.js new file mode 100644 index 00000000..cb5f0f92 --- /dev/null +++ b/src/categories.js @@ -0,0 +1,49 @@ +import data from './data/dataset.js'; //importa data +import { navigateTo } from './router.js'; + +export function renderCategories(params) { + + const selectBox = document.createElement('select'); + selectBox.setAttribute('name', 'mainField'); + selectBox.setAttribute('id', 'mainField'); + selectBox.setAttribute('data-testid', 'select-filter'); + + selectBox.addEventListener('change', function (event) { + const selectBoxOrder = document.querySelector('select#orderDir'); + navigateTo('/', { category: event.target.value, order: selectBoxOrder.value }); + }); + + const uniqueCategories = [];// array que contendra categorias unicas + + // filtrar categorias unicas + for (let i = 0; i < data.length; i++) { + + const cardData = data[i]; + for (const category of cardData['facts']['mainField']) { + if (!uniqueCategories.includes(category)) { + uniqueCategories.push(category); + } + } + } + + // agregando la primera opcion + const defaultOption = document.createElement("option"); + defaultOption.value = ''; + defaultOption.text = "- Filtrar por Categoría -"; + selectBox.add(defaultOption); + + // rellenar el selectBox con opciones de categorias unicas que construimos en arriba + for (const uniqueCat of uniqueCategories) { + const option = document.createElement("option"); + option.value = uniqueCat; + option.text = uniqueCat; + + if (params.category === uniqueCat) { + option.setAttribute('selected', true); + } + + selectBox.add(option); + } + + return selectBox; +} \ No newline at end of file diff --git a/src/data/dataset.js b/src/data/dataset.js new file mode 100644 index 00000000..ae3a2dd5 --- /dev/null +++ b/src/data/dataset.js @@ -0,0 +1,307 @@ +export default [ + { + "id": "ada-lovelace", + "name": "Ada Lovelace", + "shortDescription": "Pionera en la programación, primera programadora de la historia.", + "descripción": "Una visionaria matemática británica del siglo XIX, es aclamada como la primera programadora de la historia. Su colaboración con Charles Babbage en la creación de la Máquina Analítica la llevó a desarrollar algoritmos, incluyendo el famoso \"algoritmo de Bernoulli\", destinados a ser procesados por una máquina. Su visión pionera la convirtió en una figura fundamental en la informática y la inteligencia artificial. Lovelace demostró que las computadoras podían ir más allá de simples cálculos matemáticos, anticipando un mundo donde la programación sería esencial en la resolución de problemas complejos, dejando una huella imborrable en la ciencia y la tecnología.", + "imageUrl": "https://th.bing.com/th/id/OIG2.0ZM9XJvnSoMxH5_HSPe5?", + "facts": { + "yearOfBirth": "1815", + "yearOfDeath": "1852", + "birthPlace": "Londres, Reino Unido", + "mainField": ["Ciencias de la Computación"] + }, + + }, + { + "id": "emmy-noether", + "name": "Emmy Noether", + "shortDescription": "Matemática destacada, teorema fundamental en álgebra abstracta.", + "descripción": "Una brillante matemática alemana, revolucionó la física teórica con sus contribuciones al teorema de conservación de la energía y el teorema de Noether, fundamentales en la teoría de la relatividad y la mecánica cuántica. A pesar de los obstáculos que enfrentó como mujer en la academia, sus investigaciones en álgebra abstracta y teoría de grupos son fundamentales en la física moderna. Su capacidad para unificar conceptos matemáticos y físicos marcó un hito en la historia de la ciencia, allanando el camino para futuras generaciones de científicas.", + "imageUrl": "https://tse2.mm.bing.net/th?id=OIP.mXRahKFY0wPhgAE2t897xAHaD9&pid=Api&P=0&h=180", + "facts": { + "yearOfBirth": "1882", + "yearOfDeath": "1935", + "birthPlace": "Erlangen, Alemania", + "mainField": ["Matemáticas"] + }, + }, + { + "id": "hypatia-de-alejandria", + "name": "Hypatia de Alejandría", + "shortDescription": "Matemática y filósofa en la antigua Alejandría.", + "descripción": "Filósofa y matemática en la antigua Alejandría, desafió las normas de género de su época al destacar en una sociedad dominada por hombres. Su legado incluye importantes contribuciones a la geometría y la astronomía, así como su destacado papel como maestra y defensora del conocimiento en una época de agitación política. Hypatia demostró que las mujeres podían sobresalir en campos intelectuales y su valentía al mantener sus creencias filosóficas la convierte en un símbolo de resistencia y sabiduría en la historia de la ciencia.", + "imageUrl": "https://th.bing.com/th/id/OIG1.cJIBrDrYJvUF.VOfuaDd?", + "facts": { + "yearOfBirth": "circa 360", + "yearOfDeath": "415", + "birthPlace": "Alejandría, Egipto", + "mainField": ["Matemáticas", "Filosofía"] + } + }, + { + "id": "marjorie-lee-browne", + "name": "Marjorie Lee Browne", + "shortDescription": "Matemática, contribuyó a la teoría de números.", + "descripción": "Matemática estadounidense, destacó en una época en la que las mujeres afroamericanas enfrentaban múltiples barreras en la educación superior. No solo obtuvo su doctorado en matemáticas, sino que también se convirtió en una destacada educadora, promoviendo la diversidad en las ciencias matemáticas y desafiando la discriminación racial y de género. Su compromiso con la educación y su capacidad para inspirar a futuras generaciones la convierten en un modelo a seguir para las mujeres en la ciencia, dejando una huella perdurable en la historia académica.", + "imageUrl": "https://th.bing.com/th/id/OIG1.uNb7uipgT7cH4UqUThOl?", + "facts": { + "yearOfBirth": "1914", + "yearOfDeath": "1979", + "birthPlace": "Memphis, Estados Unidos", + "mainField": ["Matemáticas"] + }, + }, + { + "id": "shakuntala-devi", + "name": "Shakuntala Devi", + "shortDescription": "Matemática prodigio, conocida como la 'Calculadora Humana'.", + "descripción": "Conocida como \"la bruja de las matemáticas\" de la India, dejó una marca indeleble en el mundo de los números. Su capacidad mental asombrosa la llevó a resolver complejos cálculos matemáticos mentalmente en cuestión de segundos, estableciendo récords mundiales. Además de su talento innato, Devi promovió activamente la educación matemática y luchó por la igualdad de género en un país donde las mujeres enfrentaban desafíos en la ciencia y la educación. Su legado destaca el poder de la mente humana y la importancia de la igualdad de oportunidades en la ciencia.", + "imageUrl": "https://static01.nyt.com/images/2013/04/24/world/asia/devi-1/devi-1-superJumbo.jpg", + "facts": { + "yearOfBirth": "1929", + "yearOfDeath": "2013", + "birthPlace": "Bangalore, India", + "mainField": ["Matemáticas"] + }, + }, + { + "id": "sofia-kovalevskaya", + "name": "Sofia Kovalevskaya", + "shortDescription": "Matemática y escritora, contribuyó a la teoría de funciones.", + "descripción": "Matemática rusa del siglo XIX, dejó una marca indeleble en la ciencia del siglo XIX al desafiar las barreras de género. Como matemática rusa, superó las restricciones sociales de su época para convertirse en la primera mujer en obtener un doctorado en matemáticas en Europa. Sus contribuciones a la teoría de las ecuaciones diferenciales y la mecánica revolucionaron la disciplina, abriendo nuevas perspectivas en el análisis matemático. Su destacada membresía en la Academia de Ciencias de Suecia marcó un hito en la historia de las mujeres en la ciencia.", + "imageUrl": "https://th.bing.com/th/id/OIG4.gEsjmMt1in1ojPGj95e8?", + "facts": { + "yearOfBirth": "1850", + "yearOfDeath": "1891", + "birthPlace": "Moscú, Rusia", + "mainField": ["Matemáticas"] + }, + }, + { + "id": "yoko-shimomura", + "name": "Yoko Shimomura", + "shortDescription": "Compositora de música, destacada en la industria de los videojuegos.", + "descripción": "Compositora japonesa, es una figura destacada en la ciencia de la música y el arte sonoro. Aunque no se dedica a la ciencia en el sentido tradicional, su trabajo en la composición musical ha requerido un profundo entendimiento de la teoría musical y la tecnología de audio. Ha contribuido de manera significativa a la industria de los videojuegos, componiendo icónicas bandas sonoras que han enriquecido la experiencia de juego. Su éxito en un campo donde las mujeres estaban subrepresentadas la convierte en un modelo a seguir, incluso en campos no convencionales como la composición musical.", + "imageUrl": "https://th.bing.com/th/id/OIG3.tdI1vIv9Ek6kIwFFjDGg?", + "facts": { + "yearOfBirth": "1967", + "birthPlace": "Hyogo, Japón", + "mainField": ["Composición Musical", "Video Juegos"] + }, + }, + { + "id": "maryam-mirzakhani", + "name": "Maryam Mirzakhani", + "shortDescription": "Matemática, ganadora del Premio Fields.", + "descripción": "Matemática iraní, hizo historia al convertirse en la primera mujer en ganar la Medalla Fields, el premio más prestigioso en matemáticas. Sus investigaciones en la geometría de superficies y la teoría de las superficies de Riemann han tenido un impacto duradero en la matemática pura. A lo largo de su carrera, Mirzakhani inspiró a muchas mujeres a perseguir carreras en ciencia y matemáticas, derribando las barreras de género. Su enfoque innovador y su capacidad para resolver problemas complejos la convierten en un modelo a seguir para la comunidad científica y en un símbolo de la excelencia matemática.", + "imageUrl": "https://e00-elmundo.uecdn.es/assets/multimedia/imagenes/2017/07/15/15001170322481.jpg", + "facts": { + "yearOfBirth": "1977", + "yearOfDeath": "2017", + "birthPlace": "Teherán, Irán", + "mainField": ["Matemáticas"] + }, + }, + { + "id": "adele-goldberg", + "name": "Adele Goldberg", + "shortDescription": "Informática pionera, co-creadora del lenguaje de programación Smalltalk.", + "descripción": "Informática estadounidense reconocida por su influyente trabajo en la investigación y desarrollo de la programación orientada a objetos. Su contribución más destacada es la creación del lenguaje de programación Smalltalk, que revolucionó la forma en que se desarrollan software y sistemas informáticos. Goldberg ha sido una defensora incansable de la usabilidad y la accesibilidad en la informática, asegurando que las aplicaciones informáticas sean más amigables para los usuarios. Su trabajo ha allanado el camino para una programación más intuitiva y eficiente, y su legado perdura en la industria de la tecnología.", + "imageUrl": "https://d34zi8nxray0rk.cloudfront.net/102695725-03-04.jpg", + "facts": { + "yearOfBirth": "1945", + "birthPlace": "Chicago, Estados Unidos", + "mainField": ["Ciencias de la Computación"] + }, + }, + { + "id": "barbara-liskov", + "name": "Barbara Liskov", + "shortDescription": "Científica de la computación, pionera en programación orientada a objetos.", + "descripción": "Pionera de la informática, es conocida por su trabajo en la programación y la arquitectura de sistemas distribuidos. Fue la primera mujer en obtener un doctorado en ciencias de la computación en el Instituto de Tecnología de Stanford y desarrolló el lenguaje de programación CLU, que estableció fundamentos para la programación orientada a objetos. Liskov ha dejado una marca indeleble en la industria de la tecnología al liderar avances en sistemas de almacenamiento y seguridad informática. Su enfoque en la fiabilidad y la robustez ha influido en el diseño de sistemas críticos en todo el mundo.", + "imageUrl": "https://cdn.britannica.com/86/129086-004-A93A7277/Barbara-Liskov-Turing-Award-2008.jpg", + "facts": { + "yearOfBirth": "1939", + "birthPlace": "Los Angeles, Estados Unidos", + "mainField": ["Ciencias de la Computación"] + }, + }, + { + "id": "grace-hopper", + "name": "Grace Hopper", + "shortDescription": "Científica de la computación, creadora del primer compilador.", + "descripción": "Una de las pioneras de la informática, es famosa por su contribución al desarrollo del primer compilador y al lenguaje de programación COBOL. Además, desafió los estereotipos de género en la década de 1940 al unirse a la Marina de los Estados Unidos y convertirse en la primera programadora de la computadora Harvard Mark I. Su legado abarca la creación de conceptos cruciales como la \"depuración de software\" y su influencia en el desarrollo de la informática moderna, estableciendo estándares para la programación de alto nivel y la innovación tecnológica.", + "imageUrl": "https://tse2.mm.bing.net/th?id=OIP.-CiV1TmDu6RPY-qGYXRmeAHaEK&pid=Api&P=0&h=180", + "facts": { + "yearOfBirth": "1906", + "yearOfDeath": "1992", + "birthPlace": "Nueva York, Estados Unidos", + "mainField": ["Ciencias de la Computación"] + }, + }, + { + "id": "margaret-hamilton", + "name": "Margaret Hamilton", + "shortDescription": "Científica de la computación, lideró desarrollo de software para Apollo 11.", + "descripción": "Destacada científica de la computación que desempeñó un papel fundamental en el programa Apollo de la NASA. Fue directora de la División de Ingeniería de Software en el Laboratorio de Instrumentación del MIT, donde desarrolló el concepto de \"ingeniería de software\". Su liderazgo en el desarrollo del software de vuelo para las misiones Apollo fue fundamental para el éxito de la llegada del hombre a la Luna. Hamilton es un ejemplo de cómo la tecnología y la ciencia pueden alcanzar logros extraordinarios cuando se combinan con un enfoque innovador y una visión audaz.", + "imageUrl": "https://mujeresconciencia.com/app/uploads/2021/07/hamilton_port-300x168.jpg", + "facts": { + "yearOfBirth": "1936", + "birthPlace": "Paoli, Indiana, Estados Unidos", + "mainField": ["Ciencias de la Computación"] + }, + }, + { + "id": "radia-perlman", + "name": "Radia Perlman", + "shortDescription": "Ingeniera de redes, inventora del algoritmo Spanning Tree.", + "descripción": "Científica de la computación estadounidense, ha dejado una marca indeleble en el mundo de las redes informáticas y la seguridad. Conocida como \"la madre de Internet\", su invención del algoritmo Spanning Tree Protocol (STP) revolucionó la conectividad de redes y sentó las bases para la infraestructura de Internet moderna. A pesar de operar en un campo dominado por hombres, Perlman ha demostrado que las mujeres pueden liderar avances tecnológicos vitales. Su compromiso con la resiliencia de las redes y la ciberseguridad ha influido en la estabilidad y confiabilidad de la comunicación en línea.", + "imageUrl": "https://www.uspto.gov/sites/default/files/govdelivery/perlman-2-edited_crop.jpg", + "facts": { + "yearOfBirth": "1951", + "birthPlace": "Portsmouth, Estados Unidos", + "mainField": ["Ciencias de la Computación"] + }, + }, + { + "id": "shafi-goldwasser", + "name": "Shafi Goldwasser", + "shortDescription": "Científica de la computación, ganadora del Premio Turing.", + "descripción": "Es una destacada criptógrafa y científica de la computación que ha contribuido significativamente a la teoría de la computación y la seguridad de la información. Su trabajo en criptografía de probabilidad y complejidad computacional ha sentado las bases para la seguridad en línea y la privacidad de datos en un mundo cada vez más digitalizado. Goldwasser es un ejemplo de cómo la ciencia de la computación puede abordar desafíos contemporáneos y proteger la información en la era de la información.", + "imageUrl": "https://events.seas.upenn.edu/wp-content/uploads/2023/12/Shafi-Goldwasser-e1702588657339.jpg", + "facts": { + "yearOfBirth": "1958", + "birthPlace": "Nueva York, Estados Unidos", + "mainField": ["Ciencias de la Computación"] + }, + }, + { + "id": "chien-shiung-wu", + "name": "Chien-Shiung Wu", + "shortDescription": "Física experimental, contribuyó a la física nuclear.", + "descripción": "Física experimental de origen chino, desafiando los prejuicios de género y nacionalidad, realizó experimentos fundamentales en física de partículas y contribuyó significativamente a la comprensión de la desintegración beta. Su famoso experimento \"Wu\" refutó la paridad en la física de partículas, lo que cambió la forma en que los científicos comprenden las fuerzas fundamentales de la naturaleza. Wu demostró que el talento y la perseverancia pueden superar cualquier barrera, y su legado sigue siendo un faro de inspiración para científicas en todo el mundo.", + "imageUrl": "https://i.ytimg.com/vi/7PWUsiqn0bI/maxresdefault.jpg", + "facts": { + "yearOfBirth": "1912", + "yearOfDeath": "1997", + "birthPlace": "Liuhe, Taicang, China", + "mainField": ["Física"] + }, + }, + { + "id": "hedy-lamarr", + "name": "Hedy Lamarr", + "shortDescription": "Actriz e inventora, contribuyó al desarrollo de la tecnología de espectro ensanchado.", + "descripción": "Actriz austroamericana y científica, fue una mente brillante en la intersección de la tecnología y el entretenimiento. Junto con el compositor George Antheil, inventó un sistema de comunicación secreta durante la Segunda Guerra Mundial que sentó las bases para la tecnología de espectro ensanchado y la comunicación inalámbrica moderna. Lamarr demostró que las mujeres pueden destacar tanto en el cine como en la ciencia, y su legado como inventora ha sido fundamental para el desarrollo de la tecnología de comunicación inalámbrica que usamos en la actualidad.", + "imageUrl": "https://ep01.epimg.net/especiales/2018/mujeres-de-la-ciencia/img/grandes/hedy-lamarr.jpg", + "facts": { + "yearOfBirth": "1914", + "yearOfDeath": "2000", + "birthPlace": "Viena, Austria", + "mainField": ["Actuación", "Física"] + }, + }, + { + "id": "maria-goeppert-mayer", + "name": "Maria Goeppert Mayer", + "shortDescription": "Física teórica, ganadora del Premio Nobel de Física.", + "descripción": "Física teórica alemana-estadounidense, dejó una profunda huella en la física nuclear y la mecánica cuántica. Fue la segunda mujer en recibir el Premio Nobel de Física y su trabajo pionero en la estructura de capas nucleares revolucionó nuestra comprensión de los núcleos atómicos. A pesar de las barreras de género en la academia, su dedicación y talento la llevaron a contribuir significativamente a la teoría nuclear, abriendo el camino para futuras investigadoras en un campo dominado por hombres. Maria Goeppert Mayer es un ejemplo inspirador de excelencia científica y perseverancia en la búsqueda del conocimiento.", + "imageUrl": "https://www.gob.mx/cms/uploads/article/main_image/146495/Maria_Goeppert_Mayer5.jpg", + "facts": { + "yearOfBirth": "1906", + "yearOfDeath": "1972", + "birthPlace": "Kattowitz, Alemania", + "mainField": ["Física"] + }, + }, + { + "id": "lise-meitner", + "name": "Lise Meitner", + "shortDescription": "Física nuclear, contribuyó a la teoría de la fisión nuclear.", + "descripción": "Física austriaca-sueca, dejó un legado imborrable en la física nuclear y la radioquímica. A pesar de enfrentar obstáculos de género en una era dominada por hombres, colaboró con Otto Hahn en el descubrimiento de la fisión nuclear, un avance revolucionario. Aunque no compartió el Premio Nobel por este logro, su contribución fue crucial para comprender la energía nuclear y la física de partículas. Su determinación y pasión por la ciencia han inspirado a generaciones de científicas. Lise Meitner es un símbolo perdurable de la perseverancia femenina y el impacto duradero que las mujeres pueden tener en la ciencia.", + "imageUrl": "https://tse4.mm.bing.net/th?id=OIP.iSnnuD6nD-yYnUSaClZUNwHaDt&pid=Api&P=0&h=180", + "facts": { + "yearOfBirth": "1878", + "yearOfDeath": "1968", + "birthPlace": "Viena, Austria", + "mainField": ["Física"] + }, + }, + { + "id": "sau-lan-wu", + "name": "Sau Lan Wu", + "shortDescription": "Física de partículas, contribuyó a experimentos clave en física de alta energía.", + "descripción": "Física experimental sinoamericana, ha sido una fuerza motriz en la física de partículas. Destacó en experimentos en el CERN y lideró investigaciones cruciales, como el descubrimiento del quark c, que expandieron nuestro entendimiento de la estructura fundamental de la materia. Wu superó desafíos en una disciplina dominada por hombres y ha sido un faro de inspiración para científicas de todo el mundo. Sau Lan Wu es un ejemplo de excelencia científica y de cómo las mujeres pueden dejar huella en la investigación de vanguardia.", + "imageUrl": "https://kids.kiddle.co/images/thumb/f/f6/Physicist_Sau_Lan_Wu.jpg/300px-Physicist_Sau_Lan_Wu.jpg", + "facts": { + "yearOfBirth": "1940", + "birthPlace": "Hong Kong, China", + "mainField": ["Física"] + }, + }, + { + "id": "jocelyn-bell-burnell", + "name": "Jocelyn Bell Burnell", + "shortDescription": "Astrónoma y astrofísica, co-descubridora de los púlsares.", + "descripción": "Astrofísica británica, es una figura extraordinaria en la ciencia. Durante su doctorado, hizo un descubrimiento revolucionario al detectar los primeros púlsares, estrellas de neutrones altamente magnetizadas que emiten pulsos regulares de radiación. A pesar de que su asesor recibió el Premio Nobel por este descubrimiento, Bell Burnell se ha convertido en un símbolo de perseverancia y contribución de las mujeres a la astronomía. Su carrera distinguida y su activismo en favor de la diversidad de género en la ciencia han inspirado a generaciones futuras de científicas. Jocelyn Bell Burnell es un ejemplo de valentía y excelencia científica.", + "imageUrl": "https://e.rpp-noticias.io/xlarge/2018/09/10/431143_675460.jpg", + "facts": { + "yearOfBirth": "1943", + "birthPlace": "Belfast, Reino Unido", + "mainField": ["Astronomía", "Astrofísica", "Física"]//si tiene corchetes adicional es otro arreglo + }, + }, + { + "id": "dorothy-crowfoot-hodgkin", + "name": "Dorothy Crowfoot Hodgkin", + "shortDescription": "Química, ganadora del Premio Nobel de Química.", + "descripción": "Química británica, es una figura icónica en la ciencia que superó las barreras de género de su época. Su trabajo revolucionario en la cristalografía de rayos X permitió determinar la estructura de importantes biomoléculas, como la penicilina, la vitamina B12 y la insulina. Fue galardonada con el Premio Nobel de Química en 1964, siendo la tercera mujer en recibir este prestigioso reconocimiento. Hodgkin no solo fue una científica excepcional, sino también una defensora de la igualdad de género en la ciencia, allanando el camino para futuras generaciones de científicas y dejando un legado duradero en la investigación y la equidad.", + "imageUrl": "https://th.bing.com/th/id/OIG1.O7GjeOFH2XlHXVnE4Vxi?w=1024&h=1024&rs=1&pid=ImgDetMain", + "facts": { + "yearOfBirth": "1910", + "yearOfDeath": "1994", + "birthPlace": "El Cairo, Egipto / Shipston-on-Stour, Reino Unido", + "mainField": ["Química"] + }, + }, + { + "id": "marie-curie", + "name": "Marie Curie", + "shortDescription": "Física y química, ganadora de dos Premios Nobel.", + "descripción": "física y química de origen polaco y nacionalizada francesa, es una de las figuras más icónicas en la historia de la ciencia. Ganadora de dos premios Nobel (Física en 1903 y Química en 1911), Curie realizó investigaciones fundamentales sobre la radiactividad y el descubrimiento de los elementos radio y polonio. Su dedicación y valentía en la investigación científica, a pesar de los desafíos de la discriminación de género y los riesgos de la radiación, la convierten en un modelo a seguir para científicas de todo el mundo.", + "imageUrl": "https://th.bing.com/th/id/OIG4.l2e2IzPcZ43Ian1pa2IV?w=1024&h=1024&rs=1&pid=ImgDetMain", + "facts": { + "yearOfBirth": "1867", + "yearOfDeath": "1934", + "birthPlace": "Varsovia, Polonia", + "mainField": ["Física", "Química"] + }, + }, + { + "id": "marjory-stephenson", + "name": "Marjory Stephenson", + "shortDescription": "Microbióloga y bioquímica, investigó en enzimología.", + "descripción": "Bioquímica británica, brilló como científica en una época en la que las mujeres enfrentaban obstáculos muy significativos en la investigación científica. Su trabajo pionero en microbiología, enzimología y bioquímica contribuyó al entendimiento de procesos fundamentales en la vida. Fue una de las primeras mujeres en ser elegida miembro de la Royal Society, un reconocimiento a su destacada contribución a la ciencia. Además, fue una mentora y defensora de las mujeres en la ciencia, inspirando y allanando el camino para futuras generaciones.", + "imageUrl": "https://bloximages.chicago2.vip.townnews.com/theworldlink.com/content/tncms/assets/v3/editorial/4/b2/4b205063-dde3-5e6c-87d2-032d43ec47af/5e4ce7440185e.image.jpg", + "facts": { + "yearOfBirth": "1885", + "yearOfDeath": "1948", + "birthPlace": "Londres, Reino Unido", + "mainField": ["Química", "Microbiología", "Bioquímica"] + }, + }, + { + "id": "barbara-mcclintock", + "name": "Barbara McClintock", + "shortDescription": "Descubrió los elementos transponibles.", + "descripción": "Fue una genetista que descubrió los elementos transponibles, también conocidos como genes saltarines, que son segmentos de ADN que pueden moverse a diferentes posiciones dentro del genoma de una célula. Recibió el Premio Nobel de Fisiología o Medicina en 1983.", + "imageUrl": "https://tse2.mm.bing.net/th?id=OIP.RFA_DITcywlqH_yZlXWbTQHaEJ&pid=Api&P=0&h=180", + "facts": { + "yearOfBirth": "1902", + "yearOfDeath": "1992", + "birthPlace": "Hartford, EE.UU.", + "mainField": ["Química"] + }, + } +]; \ No newline at end of file diff --git a/src/imagenes/Captura de Pantalla 2024-08-25 a la(s) 18.51.19.png b/src/imagenes/Captura de Pantalla 2024-08-25 a la(s) 18.51.19.png new file mode 100644 index 00000000..189cccbe Binary files /dev/null and b/src/imagenes/Captura de Pantalla 2024-08-25 a la(s) 18.51.19.png differ diff --git a/src/imagenes/Captura de Pantalla 2024-08-25 a la(s) 19.03.12.png b/src/imagenes/Captura de Pantalla 2024-08-25 a la(s) 19.03.12.png new file mode 100644 index 00000000..369f38b9 Binary files /dev/null and b/src/imagenes/Captura de Pantalla 2024-08-25 a la(s) 19.03.12.png differ diff --git a/src/imagenes/imagen1.png b/src/imagenes/imagen1.png new file mode 100644 index 00000000..54d9b32b Binary files /dev/null and b/src/imagenes/imagen1.png differ diff --git a/src/imagenes/imagen2.png b/src/imagenes/imagen2.png new file mode 100644 index 00000000..d0f2f9be Binary files /dev/null and b/src/imagenes/imagen2.png differ diff --git a/src/imagenes/imagen3.png b/src/imagenes/imagen3.png new file mode 100644 index 00000000..00a0f268 Binary files /dev/null and b/src/imagenes/imagen3.png differ diff --git a/src/imagenes/imagen4.png b/src/imagenes/imagen4.png new file mode 100644 index 00000000..04a80f57 Binary files /dev/null and b/src/imagenes/imagen4.png differ diff --git a/src/imagenes/th.jpeg b/src/imagenes/th.jpeg new file mode 100644 index 00000000..0520f89b Binary files /dev/null and b/src/imagenes/th.jpeg differ diff --git a/thumb.png b/src/imagenes/thumb.png similarity index 100% rename from thumb.png rename to src/imagenes/thumb.png diff --git a/src/index.html b/src/index.html index fd6186f2..0dbc264a 100644 --- a/src/index.html +++ b/src/index.html @@ -1,12 +1,31 @@ - - - Dataverse Chat - - - -
    - - + + + + + + Dataverse Chat + + + + + +
    +

    MUJERES QUE INSPIRAN

    + +
    + +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/src/index.js b/src/index.js index a0e2d57c..fba8635e 100644 --- a/src/index.js +++ b/src/index.js @@ -1,19 +1,42 @@ -// En este archivo definirás tus rutas e importarás los componentes que vas a renderizar. +import Home from './views/home/Home.js'; +import Detail from './views/detail/Detail.js'; +import Error from './views/Error.js'; +import Conference from './views/conference/Conference.js'; -/* -import Example from './views/Example.js'; - -Ejemplo de definición de rutas: +// Importa otras vistas según sea necesario +import { setRootEl, setRoutes, onURLChange } from './router.js'; +// Define las rutas y las vistas asociadas const routes = { - "/": Example, - ... -} -*/ - -/* -TODO: -1.- Definir rutas en router. -2.- Pasar "root element" a router. -3.- Invocar el router para renderizar la vista correcta. -*/ \ No newline at end of file + '/': Home, + '/detail': Detail, + '/error': Error, + '/conference': Conference, + // Agrega más rutas y vistas aquí +}; + +// Configura las rutas +setRoutes(routes); + +// Asigna el elemento raíz donde se renderizarán las vistas +window.addEventListener("DOMContentLoaded", () => { + // Supongamos que tu elemento raíz en el HTML es un div con id 'root' + const rootElement = document.getElementById('root'); + setRootEl(rootElement); + + + + // Maneja la carga de la página inicial + onURLChange(window.location); + + // Escucha los cambios de URL (navegación) + window.onpopstate = () => onURLChange(window.location); + + const getBackHome = document.getElementsByTagName("header")[0]; + getBackHome.addEventListener('click', function () { + document.location.href = "/"; + }); + + + +}); \ No newline at end of file diff --git a/src/lib/apiKey.js b/src/lib/apiKey.js new file mode 100644 index 00000000..9ed2f4ba --- /dev/null +++ b/src/lib/apiKey.js @@ -0,0 +1,30 @@ +export const getApiKey = () => { + // Obtener la API KEY desde Local Storage + try { + const api_key = localStorage.getItem('apiKey'); + if (!api_key) { + throw new Error('Api Key not defined!'); + } + return api_key; + } catch (error) { + const api_key = prompt("Por favor ingresa una Open AI Key"); + + // Validar que la clave tiene la longitud correcta + if (!api_key || api_key.length <= 160) { + alert("Api Key invalida, por favor ingresa una Open AI api key"); + return null; // Detener la recursión si la clave no es válida + } + + setApiKey(api_key); + return api_key; // Retornar la clave válida + } +}; + +export const setApiKey = (key) => { + // Guardar la API KEY en Local Storage + try { + sessionStorage.setItem('apiKey', key);//permite almacenar datos de forma persistente en el navegador. + } catch (error) { + alert('Error al guardar la API KEY:', error); + } +}; diff --git a/src/lib/datafunctions.js b/src/lib/datafunctions.js new file mode 100644 index 00000000..4671d239 --- /dev/null +++ b/src/lib/datafunctions.js @@ -0,0 +1,49 @@ +export const filterData = (data,value='') => { + + // filtrar tarjetas + // Si estamos filtrando por una categoría específica + if( value ){ + return data.filter((cardData)=>cardData['facts']['mainField'].includes(value)); + } + else{ + // por defecto mostrar todos los datos + return data; + } +} + +// ORDENAR DATOS +export const sortData = (data,sortOrder='asc') => { + + // ordenar por nombre + + if( sortOrder === 'desc'){ + data.sort((a,b)=>b.name.localeCompare(a.name)); + } + else{ + // ordenar ascendiente por defecto + data.sort((a,b)=>a.name.localeCompare(b.name)); + } + + + return data; +} + +// STATS +export const computeStats = (data) => { + // Usar reduce para contar las ocurrencias de cada campo + const fieldCounts = data.reduce((acc, card) => { + card.facts.mainField.forEach(dato => { + acc[dato] = (acc[dato] || 0) + 1; + }); + return acc; + }, {}); + + const totalItems = data.length; + + // Calcular los porcentajes + const fieldPercentages = Object.fromEntries( + Object.entries(fieldCounts).map(([key, value]) => [key, `${Math.round((value / totalItems) * 100)}%`]) + ); + + return fieldPercentages; +} \ No newline at end of file diff --git a/src/router.js b/src/router.js new file mode 100644 index 00000000..f305b36f --- /dev/null +++ b/src/router.js @@ -0,0 +1,81 @@ +let ROUTES = {}; +let rootEl; + +export const setRootEl = (el) => { + rootEl = el; +}; + +export const setRoutes = (routes) => { + if (typeof routes !== 'object') { + throw new Error('Routes should be an object.'); + } + if (!routes['/error']) { + throw new Error('Routes should define an /error route.'); + } + ROUTES = routes; +}; + +const queryStringToObject = (queryString) => { + const params = new URLSearchParams(queryString); + const queryObject = {}; + for (const [key, value] of params) { + queryObject[key] = value; + } + return queryObject; +}; + +const renderView = (pathname, props = {}) => { + if (!rootEl) { + throw new Error('Root element is not set.'); + } + + // Clear the root element + rootEl.innerHTML = ''; + + // Find the correct view in ROUTES for the pathname + const view = ROUTES[pathname] || ROUTES['/error']; + + // Render the correct view passing the value of props + const viewEl = view(props); + + // Add the view element to the DOM root element + rootEl.appendChild(viewEl); +}; + +export const navigateTo = (pathname, props = {}) => { + // Update window history with pushState + window.history.pushState({}, pathname, window.location.origin + pathname + objectToQueryString(props)); + // Render the view with the pathname and props + renderView(pathname, props); +}; + +export const onURLChange = (location) => { + const { pathname, search } = location; + // Convert the search params to an object + const queryObject = queryStringToObject(search); + // Render the view with the pathname and query object + renderView(pathname, queryObject); +}; + +export const objectToQueryString = (props) => { + if (Object.keys(props).length !== 0) { + let str = '?'; + for (const i in props) { + str += i + '=' + props[i] + '&'; + } + + if (str.endsWith("&")) { + str = str.slice(0, -1); + } + + return str; + } + else { + return ''; + } +} + +// Set up the popstate event listener for back/forward navigation +window.onpopstate = () => { + onURLChange(window.location); +}; \ No newline at end of file diff --git a/src/styles/main.css b/src/styles/main.css new file mode 100644 index 00000000..3887fd60 --- /dev/null +++ b/src/styles/main.css @@ -0,0 +1,102 @@ +/* css generales tienen que estar en este archivo */ + +* { + padding: 0px; + margin: 0px; + box-sizing: border-box; + + +} + +body { + font-family: Arial, sans-serif; + /* Define la fuente */ + padding: 20px; + border: 0px; + display: flex; + flex-direction: column; + min-height: 100vh; + background-color: rgb(204, 198, 191); + + + +} + +header { + margin-bottom: 20px; + text-align: center; + /* Centra el contenido del header */ + background-image: linear-gradient(rgba(43, 3, 47, 0.35), rgba(144, 109, 137, 0.244)), url("/imagenes/imagen2.png"); + +} + +body header h1 { + margin: 30px; + /* Espacio superior */ + font-size: 3em; + /* Tamaño de fuente */ + color: rgb(255, 255, 255); + text-shadow: 2px 2px 5px rgb(136, 56, 92); + +} + +footer { + max-width: 100%; + background-image: linear-gradient(rgba(163, 146, 165, 0.35), rgba(144, 109, 137, 0.244)), url("/imagenes/imagen4.png"); + padding: 20px; + bottom: 0%; + text-align: center; + color: rgb(255, 255, 255); + text-shadow: 2px 2px 5px rgb(136, 56, 92); + font-size: 2em +} + +#curious_fact { + max-width: 100%; + background-image: linear-gradient(rgba(163, 146, 165, 0.35), rgba(144, 109, 137, 0.244)), url("/imagenes/imagen1.png"); + padding: 10px; + bottom: 0%; + text-align: center; + margin: 10px; + color: rgb(36, 35, 35); + text-shadow: 2px 2px 5px rgb(136, 56, 92); + font-size: 2em; +} + +h3 { + color: rgb(3, 2, 3); + text-shadow: 2px 2px 5px rgb(246, 242, 242); +} + +#curious_fact div { + margin: 30px; +} + +.hidden { + display: none; +} + +.apikey { + font-size: 12px; + color: #000; + position: absolute; + top: 30px; + right: 30px; +} + +button { + text-decoration: none; + display: inline-block; + padding: 8px 16px; + border-radius: 5%; +} + +button:hover { + background-color: rgb(255, 255, 255); + color: rgb(198, 136, 191); +} + +#return-home { + background-color: #f1f1f1; + color: black; +} diff --git a/src/view.js b/src/view.js new file mode 100644 index 00000000..37b20793 --- /dev/null +++ b/src/view.js @@ -0,0 +1,15 @@ +import { renderCard } from './card.js'; // importa vista + +export const renderItems = (data) => { + // Crear una cadena concatenando los outerHTML de cada tarjeta renderizada + return data.map(item => renderCard(item)); +} + +export const renderStats = (fieldPercentages) => { + + return ` + Ciencias de la Computación: ${fieldPercentages['Ciencias de la Computación'] || '0%'} , + Matemáticas: ${fieldPercentages['Matemáticas'] || '0%'} , + Química: ${fieldPercentages['Química'] || '0%'} + `.trim(); +} \ No newline at end of file diff --git a/src/views/Error.js b/src/views/Error.js new file mode 100644 index 00000000..7619e2a5 --- /dev/null +++ b/src/views/Error.js @@ -0,0 +1,9 @@ +const Error = () => { + const view = document.createElement('div'); + view.innerHTML = ` +

    Ops.. esto es incomodo pero no encontre la pagina solicitada

    + `; + return view; +}; + +export default Error; \ No newline at end of file diff --git a/src/views/Example.js b/src/views/Example.js deleted file mode 100644 index baca4441..00000000 --- a/src/views/Example.js +++ /dev/null @@ -1,5 +0,0 @@ -const Example = () => { - -}; - -export default Example; \ No newline at end of file diff --git a/src/views/chat/Chat.js b/src/views/chat/Chat.js new file mode 100644 index 00000000..4896ce13 --- /dev/null +++ b/src/views/chat/Chat.js @@ -0,0 +1,84 @@ +import * as OpenAI from 'https://esm.run/openai'; +import { getApiKey } from '/lib/apiKey.js'; + + +function getContext(names, allCharacters) { + if (!allCharacters) { + return "Eres " + names + " y responderas segun su historia y como si fueras ella"; + } else { + return "Eres una conferencia de mujeres historicamente importante donde estarian " + names + " responde personificando a las mujeres como una voz en conjuto y solo en español"; + } +} + +async function callOpenAIApi(context, text) { + const openai = new OpenAI.OpenAI({ + apiKey: getApiKey(), + dangerouslyAllowBrowser: true + } + ); + const chatCompletion = await openai.chat.completions.create({ + messages: [ + { role: "system", content: context }, + { role: 'user', content: text }], + model: 'gpt-4o', + }); + const getChatBox = document.getElementsByClassName('messages')[0]; + const respond = document.createElement('li'); + respond.classList.add('her'); + respond.appendChild(document.createTextNode(chatCompletion.choices[0].message.content)); + getChatBox.appendChild(respond); +} + + +export const renderChat = (data, allCharacters = false) => { + getApiKey(); + const view = document.createElement('div'); + view.classList.add('chat-container'); // Add container class + + const right = document.createElement('div'); + right.classList.add('chat-box'); // Changed class name to chat-box + + const title = document.createElement('h2'); + title.textContent = allCharacters ? "Chatea con nosotras:" : "Chatea Conmigo:"; + right.appendChild(title); + + const chatBox = document.createElement('ul'); + chatBox.classList.add('messages'); + right.appendChild(chatBox); + + const writeDiv = document.createElement('div'); + writeDiv.classList.add('write'); + + const textarea = document.createElement('textarea'); + textarea.setAttribute('name', 'message'); + textarea.setAttribute('rows', 4); + textarea.classList.add('message-input'); // Added class for styling + writeDiv.appendChild(textarea); + + const button = document.createElement('button'); + button.setAttribute('type', 'button'); + button.setAttribute('id', 'send'); + button.textContent = 'ENVIAR'; + button.classList.add('send-button'); // Added class for styling + writeDiv.appendChild(button); + + button.addEventListener('click', function () { + const messageBox = document.querySelector('textarea[name="message"]'); + const names = allCharacters ? data.map(data => data.name).join(', ') : data['name']; + + const me_ask = document.createElement('li'); + me_ask.classList.add('me'); + me_ask.appendChild(document.createTextNode(messageBox.value)); + chatBox.appendChild(me_ask); + + const assistant_context = getContext(names, allCharacters); + callOpenAIApi(assistant_context, messageBox.value); + messageBox.value = ""; + messageBox.focus(); + }); + + right.appendChild(writeDiv); + view.appendChild(right); + + return view; +} diff --git a/src/views/conference/Conference.js b/src/views/conference/Conference.js new file mode 100644 index 00000000..b18ef1ef --- /dev/null +++ b/src/views/conference/Conference.js @@ -0,0 +1,23 @@ +import data from '/data/dataset.js'; //importa data +import { renderChat } from '/views/chat/Chat.js'; + + +const Conference = () => { + // habilitar el botton volver a pagina principal + const returnHomeButton = document.getElementById('return-home'); + returnHomeButton.classList.remove('hidden'); + + const view = document.createElement('div'); + + const head = document.getElementsByTagName('head')[0]; + const link = document.createElement('link'); + // Set the necessary attributes + link.rel = 'stylesheet'; + link.type = 'text/css'; + link.href = '/views/conference/style.css'; + head.appendChild(link); + view.append(renderChat(data, true)); + return view; +}; + +export default Conference; \ No newline at end of file diff --git a/src/views/conference/style.css b/src/views/conference/style.css new file mode 100644 index 00000000..20eb2860 --- /dev/null +++ b/src/views/conference/style.css @@ -0,0 +1,31 @@ +.detail-card { + display: flex; + width: 100%; +} + +.card { + flex: 0 0 calc(30% - 20x); + background-color: lightblue; + padding: 10px; + margin: 0px; + list-style-type: none; +} + +.card img { + width: 100%; + flex: auto; + padding: flex; +} + +.chat { + display: flex; + flex: 0 0 calc(90% - 50px); + background-color: lightblue; + padding: 20px 30px 20px 20px; +} + +.write textarea, +.write button { + width: 90%; + margin: 20px; +} \ No newline at end of file diff --git a/src/views/detail/Detail.js b/src/views/detail/Detail.js new file mode 100644 index 00000000..951114c2 --- /dev/null +++ b/src/views/detail/Detail.js @@ -0,0 +1,44 @@ +import data from '/data/dataset.js'; //importa data +import { renderCard } from '/card.js'; +import { renderChat } from '/views/chat/Chat.js'; + + +const ExtractDataDetailById = (id) => { + return data.filter((el) => { return el.id === id; })[0] || []; +}; + +const Detail = (params) => { + // habilitar el botton volver a pagina principal + const returnHomeButton = document.getElementById('return-home'); + returnHomeButton.classList.remove('hidden'); + + const cardData = ExtractDataDetailById(params['id']); + const view = document.createElement('div'); + + const head = document.getElementsByTagName('head')[0]; + const link = document.createElement('link'); + // Set the necessary attributes + link.rel = 'stylesheet'; + link.type = 'text/css'; + link.href = '/views/detail/style.css'; + head.appendChild(link); + + + if (cardData.length === 0) { + // si se escribe mal el id, se devolvera un error y no se renderizara la pagina + view.innerHTML = ` +

    Disculpa! no logre encontrar a la persona que buscas

    + /home/home.js + `; + } else { + view.classList.add('detail-card'); + + view.append(renderCard(cardData, true)); + view.append(renderChat(cardData)); + + + } + return view; +}; + +export default Detail; \ No newline at end of file diff --git a/src/views/detail/style.css b/src/views/detail/style.css new file mode 100644 index 00000000..748afa89 --- /dev/null +++ b/src/views/detail/style.css @@ -0,0 +1,111 @@ +.detail-card { + display: flex; + width: 100%; +} + +.card { + flex: 0 0 calc(50% - 50x); + background-color: rgb(247, 231, 244); + padding: 20px; + list-style-type: none; + width: 500px; + margin: auto; +} + +.card li.item img { + height: 300px; + width: 450px; + object-fit: cover; +} + +.chat { + display: flex; + flex: 0 0 calc(60% - 50px); + background-color: lightblue; + padding: 20px 30px 20px 20px; +} + +.write textarea, +.write button { + width: 90%; + margin: 20px; +} + + + +.chat-container { + width: 100%; + max-width: 600px; + margin: 0 auto; + display: flex; + flex-direction: column; + justify-content: space-between; + height: 55vh; + /* Gives the chat container a fixed height */ + border: 1px solid #ddd; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); +} + +.chat-box { + flex-grow: 1; + display: flex; + flex-direction: column; + padding: 10px; + background-color: #ffffff; + +} + +.messages { + background-image: linear-gradient(rgba(207, 159, 212, 0.35), rgba(235, 198, 233, 0.732)); + padding: 20px; + list-style: none; + padding: 0; + margin: 0; + flex-grow: 1; + overflow-y: auto; + /* Allows scrolling for long chats */ + max-height: 60vh; + /* Restrict chatbox height */ +} + +.me { + text-align: right; + margin-bottom: 5px; +} + +.her { + text-align: left; + margin-bottom: 5px; +} + + +.write { + display: flex; + padding: 10px; + border-top: 1px solid #ddd; + background-color: white; +} + +.message-input { + width: 80%; + padding: 10px; + border: 1px solid #ddd; + border-radius: 5px; + font-size: 16px; +} + +.send-button { + width: 20%; + margin-left: 10px; + padding: 10px; + background-color: rgb(227, 137, 215); + color: rgb(10, 11, 11); + border: none; + border-radius: 5px; + font-size: 16px; + cursor: pointer; +} + +.send-button:hover { + background-color: #f6d8f1; +} \ No newline at end of file diff --git a/src/views/home/Home.js b/src/views/home/Home.js new file mode 100644 index 00000000..1d5a72ea --- /dev/null +++ b/src/views/home/Home.js @@ -0,0 +1,102 @@ +import data from '../../data/dataset.js'; //importa data +import { renderItems, renderStats } from '../../view.js'; //importa view +import { filterData, sortData, computeStats } from '../../lib/dataFunctions.js'; +import { renderCategories } from '../../categories.js'; //importa categories +import { navigateTo } from '../../router.js'; + +// aqui van las tarjetas +const Home = (params) => { + + const categorySelect = renderCategories(params); + + const controls = document.createElement('div'); + + const filterControls = document.createElement('div'); + filterControls.classList.add('filter-controls'); + + const catLabel = document.createElement('label'); + catLabel.setAttribute('for', 'mainField'); + catLabel.textContent = 'Categoría'; + filterControls.appendChild(catLabel); + + filterControls.appendChild(categorySelect); + + const sortLabel = document.createElement('label'); + sortLabel.setAttribute('for', 'orderDir'); + sortLabel.textContent = 'Ordenar'; + filterControls.appendChild(sortLabel); + + const orderSelect = document.createElement('select'); + orderSelect.setAttribute('id', 'orderDir'); + orderSelect.setAttribute('name', 'orderDir'); + orderSelect.setAttribute('data-testid', 'select-sort'); + + const availableOptions = [{ value: 'asc', text: 'Ascendente' }, { value: 'desc', text: 'Descendente' }]; + + for (const a of availableOptions) { + const option = document.createElement("option"); + option.value = a.value; + option.text = a.text; + if (params.order === a.value) { + option.setAttribute('selected', true); + } + + orderSelect.add(option); + } + // Añadir event listeners para la navegación + orderSelect.addEventListener('change', function (event) { + const selectBoxCategory = document.querySelector('select#mainField'); + navigateTo('/', { category: selectBoxCategory.value, order: event.target.value }); + }); + + filterControls.appendChild(orderSelect); + + const cleanButton = document.createElement('button'); + cleanButton.setAttribute('type', 'button'); + cleanButton.setAttribute('id', 'clean'); + cleanButton.textContent = 'Limpiar'; + + cleanButton.addEventListener('click', function () { + navigateTo('/'); + }); + + filterControls.appendChild(cleanButton); + controls.appendChild(filterControls); + + let items; + + if (params.category !== '') { + const filtered = filterData(data, params.category); + items = renderItems(sortData(filtered, params.order)); + } + else { + items = renderItems(sortData(data, params.order)); + } + + const view = document.createElement('div'); + + const link = document.createElement('link'); + // Set the necessary attributes + link.rel = 'stylesheet'; + link.type = 'text/css'; + link.href = './views/home/style.css'; // Replace with the path to your CSS file + view.appendChild(link); + + view.appendChild(controls); + + const cardsRoot = document.createElement('div'); + cardsRoot.setAttribute('id', 'cards_root'); + + for (const card of items) { + cardsRoot.appendChild(card); + } + + view.appendChild(cardsRoot); + + const facts = document.querySelector("#curious_fact"); + facts.textContent = renderStats(computeStats(data)); + + return view; +}; + +export default Home; \ No newline at end of file diff --git a/src/views/home/style.css b/src/views/home/style.css new file mode 100644 index 00000000..181847a5 --- /dev/null +++ b/src/views/home/style.css @@ -0,0 +1,121 @@ + #root, + #root>div, + #cards_root { + display: flex; + flex-wrap: wrap; + gap: 40px; + justify-content: center; + } + + #cards_root { + display: flex; + flex-wrap: wrap; + gap: 10px; + justify-content: center; + } + + #root>.detail-card { + justify-content: left; + } + + .card { + margin: 10px; + border: 2px solid #ddd; + /* Borde de la tarjeta */ + border-radius: 30px 30px 30px 30px; + /* Bordes redondeados */ + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + /* Sombra */ + background: rgba(233, 220, 233, 0.769); + /* Color de fondo de las tarjetas */ + padding: 20px; + text-align: left; + flex: 1 0 calc(25% - 40px); + /* Cada tarjeta ocupará un 40% del ancho del contenedor, menos el espacio del gap */ + box-sizing: border-box; + max-width: calc(20% - 60px); + /* Limitar el ancho máximo de cada tarjeta */ + display: flex; + flex-wrap: wrap; + list-style-type: none; + /* Elimina los puntos de la lista */ + padding: 0; + /* Elimina el padding */ + font-size: 1.1em; + justify-content: space-between; + overflow: hidden; + } + + .card li.item { + margin-bottom: 10px; + /* Espaciado inferior entre ítems */ + display: flex; + color: rgb(45, 39, 45); + padding-left: 10px; + } + + .card li.item:first-of-type { + width: 100%; + align-self: center; + padding-top: 10px; + color: rgb(45, 39, 45); + } + + .card li.item img { + margin-top: -15px; + margin-left: -10px; + height: 200px; + width: 290px; + object-fit: cover; + } + + select { + background-color: #3735356c; + color: rgb(14, 12, 14); + text-shadow: 2px 2px 5px rgb(225, 208, 208); + font-size: 1.1em; + margin: 30px; + /* Añade separación al botón que está al lado */ + + + + } + + label { + color: rgb(7, 6, 7); + text-shadow: 2px 2px 5px rgb(234, 220, 220); + font-size: 1.3em; + margin-left: 20px; + + } + + button { + color: rgb(95, 104, 109); + text-shadow: #4c2f53; + color: rgb(14, 12, 14); + text-shadow: 2px 2px 5px rgb(250, 236, 236); + font-size: 15px; + margin: 40px; + + + } + + @media (max-width: 900px) { + .card { + flex: 1 1 calc(50% - 40px); + /* 50% del ancho menos el espacio del gap */ + max-width: calc(50% - 40px); + } + } + + @media (max-width: 600px) { + .card { + flex: 1 1 calc(100% - 40px); + /* 100% del ancho menos el espacio del gap */ + max-width: calc(100% - 40px); + } + } + + .detail-card { + justify-content: center; + } \ No newline at end of file diff --git a/test/apiKey.spec.js b/test/apiKey.spec.js new file mode 100644 index 00000000..19820869 --- /dev/null +++ b/test/apiKey.spec.js @@ -0,0 +1,34 @@ +import { getApiKey, setApiKey } from '../src/lib/apiKey.js'; + +describe('setApiKey', () => { + it('debería establecer correctamente la API Key en localStorage', () => { + // Simular el almacenamiento en localStorage + const key = 'test-api-key'; + setApiKey(key); + expect(localStorage.getItem('apiKey')).toBe(key); + }); +}); + +describe('getApiKey', () => { + beforeEach(() => { + localStorage.clear(); // Limpiar localStorage antes de cada test + }); + + it('debería devolver el valor de la API Key desde localStorage', () => { + // Configurar una clave de API en localStorage + const key = 'test-api-key'; + localStorage.setItem('apiKey', key); + + const apiKey = getApiKey(); + expect(apiKey).toBe(key); + }); + + it('debería lanzar un error si la API Key no está definida en localStorage', () => { + jest.spyOn(window, 'prompt').mockReturnValue('invalid-key'); + jest.spyOn(window, 'alert').mockImplementation(() => {}); + + const apiKey = getApiKey(); + expect(apiKey).toBeNull(); + expect(window.alert).toHaveBeenCalledWith('Api Key invalida, por favor ingresa una Open AI api key'); + }); +}); \ No newline at end of file diff --git a/test/dataFunctions.spec.js b/test/dataFunctions.spec.js new file mode 100644 index 00000000..b66f7aa3 --- /dev/null +++ b/test/dataFunctions.spec.js @@ -0,0 +1,74 @@ +import { filterData, sortData, computeStats } from '../src/lib/dataFunctions.js'; + +// Test de filterData +describe('filterData', () => { + const sampleData = [ + { + id: "ada-lovelace", + name: "Ada Lovelace", + facts: { mainField: ["Ciencias de la Computación"] } + }, + { + id: "emmy-noether", + name: "Emmy Noether", + facts: { mainField: ["Matemáticas"] } + } + ]; + + it('debería filtrar correctamente por el campo "Matemáticas"', () => { + const filtered = filterData(sampleData, 'Matemáticas'); + expect(filtered.length).toBe(1); + expect(filtered[0].name).toBe("Emmy Noether"); + }); + + it('debería devolver todos los datos si no se especifica un valor', () => { + const filtered = filterData(sampleData); + expect(filtered.length).toBe(2); + }); + describe('sortData', () => { + const sampleData = [ + { id: "ada-lovelace", name: "Ada Lovelace" }, + { id: "emmy-noether", name: "Emmy Noether" }, + ]; + + it('debería ordenar los datos en orden ascendente por defecto', () => { + const sorted = sortData([...sampleData]); + expect(sorted[0].name).toBe("Ada Lovelace"); + expect(sorted[1].name).toBe("Emmy Noether"); + }); + + it('debería ordenar los datos en orden descendente cuando se especifica', () => { + const sorted = sortData([...sampleData], 'desc'); + expect(sorted[0].name).toBe("Emmy Noether"); + expect(sorted[1].name).toBe("Ada Lovelace"); + }); + }); +}); + + +describe('computeStats', () => { + const sampleData = [ + { + id: "ada-lovelace", + name: "Ada Lovelace", + facts: { mainField: ["Ciencias de la Computación"] } + }, + { + id: "emmy-noether", + name: "Emmy Noether", + facts: { mainField: ["Matemáticas"] } + }, + { + id: "hypatia-de-alejandria", + name: "Hypatia de Alejandría", + facts: { mainField: ["Matemáticas", "Filosofía"] } + } + ]; + + it('debería calcular correctamente las estadísticas de los campos', () => { + const stats = computeStats(sampleData); + expect(stats['Ciencias de la Computación']).toBe('33%'); + expect(stats['Matemáticas']).toBe('67%'); + expect(stats['Filosofía']).toBe('33%'); + }); +}); \ No newline at end of file