diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..6f3a2913 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "liveServer.settings.port": 5501 +} \ No newline at end of file diff --git a/README.md b/README.md index 04706ddd..d10a3b8e 100644 --- a/README.md +++ b/README.md @@ -2,640 +2,66 @@ ## Índice -* [1. Preámbulo](#1-preámbulo) +* [1. Data Lovers: Ghibli Data](#1-data-lovers-ghibli-data) * [2. Resumen del proyecto](#2-resumen-del-proyecto) * [3. Objetivos de aprendizaje](#3-objetivos-de-aprendizaje) * [4. Consideraciones generales](#4-consideraciones-generales) * [5. Criterios de aceptación mínimos del proyecto](#5-criterios-de-aceptación-mínimos-del-proyecto) * [6. Hacker edition](#6-hacker-edition) -* [7. Consideraciones técnicas](#7-consideraciones-técnicas) -* [8. Pistas, tips y lecturas complementarias](#8-pistas-tips-y-lecturas-complementarias) -* [9. Checklist](#9-checklist) *** +![Banner-data-lovers](./src/img/Data%20Lovers%20Ghibli.png) -## 1. Preámbulo +## 1. Data Lovers: Ghibli Data -Según [Forbes](https://www.forbes.com/sites/bernardmarr/2018/05/21/how-much-data-do-we-create-every-day-the-mind-blowing-stats-everyone-should-read), -el 90% de la data que existe hoy ha sido creada durante los últimos dos años. -Cada día generamos 2.5 millones de terabytes de datos, una cifra sin -precedentes. +Somos Ana Sofía Lozano y Carolina Moreno y este es **Data Lovers Ghibli**, un proyecto que surgió de nuestra profunda admiración por el mundo mágico y conmovedor de Studio Ghibli. En esta página podrás explorar la data de Ghibli proporcionada por Laboratoria de una forma especial. -No obstante, los datos por sí mismos son de poca utilidad. Para que esas -grandes cantidades de datos se conviertan en **información** fácil de leer para -los usuarios, necesitamos entender y procesar estos datos. Una manera simple de -hacerlo es creando _interfaces_ y _visualizaciones_. +Nuestra misión es simple: queremos dar a los fans de Ghibli una plataforma donde puedas descubrir datos intrigantes sobre las películas, personajes, directores y mucho más. Cada vez que nos visites, queremos que sientas que estás teniendo una experiencia hermosa y enriquecedora, llena de información fascinante sobre tus películas favoritas de Studio Ghibli. -En la siguiente imagen, podrás ver cómo con la data que que se ve en la parte -izquierda se puede construir una interfaz amigable y entendible por las -usuarias, al lado derecho. +## 2. Funcionalidad +### Filtrado por Categoría: +Encuentra rápidamente la información que deseas explorar al filtrar los datos por categoría. Ya sea que estés interesado en directores, productores o personajes de las películas de Studio Ghibli, puedes acceder a los detalles de manera sencilla y rápida. -![pokemon-data-to-ui](https://user-images.githubusercontent.com/12631491/218505816-c6d11758-9de4-428f-affb-2a56ea4d68c4.png) +### Listados Detallados: + Explora a fondo cada categoría con listados detallados que te proporcionan una visión completa de los personajes, directores y productores que forman parte del mágico mundo de Ghibli. Obtén información exhaustiva sobre cada uno de ellos. -## 2. Resumen del proyecto +### Ordenen Versatil: +Organiza la información de las animaciones según diferentes criterios. Ordena las películas por año, en orden alfabético o por puntuación para explorarlas de manera organizada y personalizada. -En este proyecto **construirás una _página web_ para visualizar un -_conjunto (set) de datos_** que se adecúe a lo que descubras que tu usuario -necesita. +### Detalles de la Data: +Accede a detalles exhaustivos de cada animación, incluyendo su descripción, fecha de lanzamiento, peso y el talentoso equipo creativo detrás de ella. Sumérgete en la riqueza de información de tus películas favoritas. -Como entregable final tendrás una página web que permita **visualizar la data, -filtrarla, ordenarla y hacer algún cálculo agregado**. Con cálculo agregado -nos referimos a distintos cálculos que puedes hacer con la data para mostrar -información aún más relevante para los usuarios (promedio, el valor máximo -o mínimo, etc). +### Recuento de Elementos: +Obtén una visión clara de la amplitud de cada categoría al conocer la cantidad de elementos que se encuentran en ella. Esto te ayudará a comprender la diversidad y riqueza de Studio Ghibli. -Esta vez te proponemos una serie de datos de diferentes _temáticas_ para que -explores y decidas con qué temática te interesa trabajar. Hemos elegido -específicamente estos sets de datos porque creemos que se adecúan bien a esta -etapa de tu aprendizaje. +### Promedio de Puntuaciones: +Descubre el promedio de puntuaciones otorgadas a los directores y productores, lo que te permitirá apreciar la calidad del trabajo de cada uno. Evalúa su impacto en el mundo de la animación de Ghibli de una manera objetiva. -Una vez que definas tu área de interés, buscar entender quién es tu usuario -y qué necesita saber o ver exactamente; luego podrás construir la interfaz que -le ayude a interactuar y entender mejor esos datos. +## 3. Historias de Usuaria +A partir de una detallada investigación de usuarias, concluimos que sus necesidades y criterios de aceptación son los siguientes: +1. Como usuaria amante de películas de Studio Ghibli me gustaria ver el total de películas que se encuentra actualmente publicadas. Por cada película quiero ver su portada, título y descripción, además de contar con la información adicional de su productor, director, la puntuación y su año de publicación. -Estos son datos que te proponemos: +2. Como usuaria que consume con regularidad películas de Studio Ghibli, quiero encontrar un apartado especial dentro de la ficha de películas donde pueda encontrar los personajes principales, secundarios, locaciones y vehículos por cada película. -* [Pokémon](src/data/pokemon/pokemon.json): - En este set encontrarás una lista con los 251 Pokémon de la región de Kanto - y Johto, junto con sus respectivas estadísticas usadas en el juego - [Pokémon GO](http://pokemongolive.com). - - [Investigación con jugadores de Pokémon Go](src/data/pokemon/README.md) +3. Como usuaria fan de todo el trabajo de Studio Ghibli me encantaría poder filtrar las películas por año, por director y productor, además que todos estos filtros funcionen tanto de forma individual como de forma conjunta. Además para mejorar la experiencia, sería óptimo encontrar un botón para limpiar filtros que me muestre nuevamente el total de películas. -* [League of Legends - Challenger leaderboard](src/data/lol/lol.json): - Este set de datos muestra la lista de campeones en una liga del - juego League of Legends (LoL). - - [Investigación con jugadores de LoL](src/data/lol/README.md) +4. Finalmente como usuaria fiel a Studio Ghibli, me gustaría ver en algún apartado especial el promedio del puntaje por película. -* [Rick and Morty](src/data/rickandmorty/rickandmorty.json). - Este set nos proporciona la lista de los personajes de la serie Rick and - Morty. [API Rick and Morty](https://rickandmortyapi.com). - - [Investigación con seguidores de Rick and Morty](src/data/rickandmorty/README.md) +## 4. Prototipos (Alta y Baja) +Nuestro trabajo nos realizar varios prototipos para irnos ajustando a nuestra visión de la página +Comenzamos con algo sencillo +![Protopipo-1](/src/img/prototipo-baja-fidelidad.png) +Luego utilizamos Canva para ir proyectando dónde iría cada elemento y función según nuestras historias de usuario. +![Prototipo-2](/src/img/1.png) -* [Juegos Olímpicos de Río de Janeiro](src/data/athletes/athletes.json). - Este set nos proporciona la lista de los atletas que ganaron medallas en las - olímpiadas de Río de Janeiro. - - [Investigación con interesados en juegos olímpicos de Río de Janeiro](src/data/athletes/README.md) +Luego aterrizamos a cómo queríamos nuestra página página y ver cómo interactuarían los elementos, por ejemplo el proyecto carrusel. +![prototipo-3](/src/img/5.png) +![prototipo-4](/src/img/6.png) +## 5. Demos -* [Studio Ghibli](src/data/ghibli/ghibli.json). - En este set encontrarás una lista de las animaciones y sus personajes del - [Studio Ghibli](https://ghiblicollection.com/). - - [Investigación con seguidores de las animaciones del Studio Ghibli](src/data/ghibli/README.md) +*pendiente* -* [Countries](src/data/countries/countries.json). - En este set encontrarás la informacion de varios paises - - [Investigación](src/data/countries/README.md) +## 6. Contactos +Si tienes ideas creativas, sugerencias o encuentras cualquier problema mientras exploras nuestro sitio, por favor, no dudes en contactarnos. Puedes abrir issues o escribirnos directamente. -* [Tarot](src/data/tarot/tarot.json). - En este set encontrarás la informacion acerca de algunas cartas de tarot. - - [Investigación](src/data/tarot/README.md) - -* [Breaking Bad](src/data/breakingbad/breakingbad.json). - En este set encontrarás la informacion sobre los personajes de la serie breakingbad. - - [Investigación con los seguidores de la serie Breaking Bad](src/data/breakingbad/README.md) - -* [Juego de Tronos](src/data/got/got.json). - En este set encontrarás la informacion sobre algunos de los personajes de la serie GOT. - - [Investigación con los seguidores de la serie GOT](src/data/got/README.md) - -El objetivo principal de este proyecto es que aprendas a diseñar y construir una -interfaz web donde se pueda visualizar y manipular data, entendiendo lo que el -usuario necesita. - -El objetivo principal de este proyecto es que aprendas a diseñar y construir una -interfaz web donde se pueda visualizar y manipular data, entendiendo lo que el -usuario necesita. - -## 3. Objetivos de aprendizaje - -Reflexiona y luego marca los objetivos que has llegado a entender y aplicar en tu proyecto. Piensa en eso al decidir tu estrategia de trabajo. - -### HTML - -- [ ] **Uso de HTML semántico** - -
Links

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

- -- [ ] **Variables (declaración, asignación, ámbito)** - -
Links

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

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

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

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

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

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

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

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

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

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

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

- -- [ ] **Uso de linter (ESLINT)** - -- [ ] **Uso de identificadores descriptivos (Nomenclatura y Semántica)** - -- [ ] **Diferenciar entre expresiones (expressions) y sentencias (statements)** - -### Control de Versiones (Git y GitHub) - -- [ ] **Git: Instalación y configuración** - -- [ ] **Git: Control de versiones con git (init, clone, add, commit, status, push, pull, remote)** - -- [ ] **Git: Integración de cambios entre ramas (branch, checkout, fetch, merge, reset, rebase, tag)** - -- [ ] **GitHub: Creación de cuenta y repos, configuración de llaves SSH** - -- [ ] **GitHub: Despliegue con GitHub Pages** - -
Links

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

- -- [ ] **GitHub: Colaboración en Github (branches | forks | pull requests | code review | tags)** - -### 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) -

- -## 4. Consideraciones generales - -* Este proyecto se debe resolver en duplas. -* El rango de tiempo estimado para completar el proyecto es de 3 a 4 Sprints. -* El proyecto será entregado subiendo tu código a GitHub (commit/push) y la - interfaz será desplegada usando [GitHub Pages](https://pages.github.com/). - -## 5. Criterios de aceptación mínimos del proyecto - -Los criterios para considerar que has completado este proyecto son: - -### 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 usuario. - -### Historias de usuario - -Una vez que entiendas las necesidades de tus usuarios, escribe las [Historias -de Usuario](https://es.wikipedia.org/wiki/Historias_de_usuario) que representen -todo lo que el usuario necesita hacer/ver. Las **Historias de Usuario** deben -ser el resultado de tu proceso de investigación o _research_ de tus usuarios. - -Asegúrate de incluir la definición de terminado (_definition of done_) y los -Criterios de Aceptación para cada una. - -En la medida de lo posible, termina una historia de usuario antes de pasar -a la siguiente (Cumple con Definición de Terminado + Criterios de Aceptación). - -### Diseño de la Interfaz de Usuario - -#### 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`. - -#### Prototipo de alta fidelidad - -Lo siguiente es diseñar tu Interfaz de Usuario (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, PowerPoint, Keynote, 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](https://coda.io/d/Bootcamp-UX-Contenido_dqkqk2rV9Z2/Diseno-de-interfaces_suOT7#_luWsQ)_. - -#### Testeos de usabilidad - -Durante el reto deberás hacer _tests_ de usabilidad con distintos usuarios, y -en base a 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 Usuario (HTML/CSS/JS) - -Luego de diseñar tu interfaz de usuario 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 - -Como mínimo, tu implementación debe: - -1. Mostrar la data en una interfaz: puede ser un card, una tabla, una lista, - etc. -2. Permitir al usuario interactuar para obtener la infomación que necesita. -3. Ser _responsive_, es decir, debe visualizarse sin problemas desde distintos - tamaños de pantallas: móviles, tablets y desktops. -4. Que la interfaz siga los fundamentos de _visual design_. - -### 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. - -Tus _pruebas unitarias_ deben dar una cobertura del 70% de _statements_ -(_sentencias_), _functions_ (_funciones_), _lines_ (_líneas_), y _branches_ -(_ramas_) del archivo `src/data.js` que contenga tus funciones y está detallado -en la sección de [Consideraciones técnicas](#srcdatajs). - -## 6. Hacker edition - -Las secciones llamadas _Hacker Edition_ son **opcionales**. Si **terminaste** -con todo lo anterior y te queda tiempo, intenta completarlas. Así podrás -profundizar y/o ejercitar más sobre los objetivos de aprendizaje del proyecto. - -Features/características extra sugeridas: - -* En lugar de consumir la data estática brindada en este repositorio, puedes - consumir la data de forma dinámica, cargando un archivo JSON por medio de - `fetch`. La carpeta `src/data` contiene una versión `.js` y una `.json` de - de cada set datos. -* Agregarle a tu interfaz de usuario implementada visualizaciones gráficas. Para - ello te recomendamos explorar librerías de gráficas como - [Chart.js](https://www.chartjs.org/) - o [Google Charts](https://developers.google.com/chart/). -* 100% Coverage - -## 7. 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), -con la excepción de librerías para hacer gráficas (charts); ver -[_Parte opcional_](#6-hacker-edition) más arriba. - -El _boilerplate_ contiene una estructura de archivos como punto de partida así -como toda la configuración de dependencias: - -```text -. -├── EXTRA.md -├── README.md -├── package.json -├── src -| ├── data (según con qué data trabajes) -| | ├── lol -| | | ├── lol.js -| | | ├── lol.json -| | | └── README.md -| | ├── pokemon -| | | ├── pokemon.js -| | | ├── pokemon.json -| | | └── README.md -| | └── rickandmorty -| | | ├── rickandmorty.js -| | | ├── rickandmorty.json -| | | └── README.md -| | └── athletes -| | | ├── athletes.js -| | | ├── athletes.json -| | | └── README.md -| | └── ghibli -| | | ├── ghibli.js -| | | ├── ghibli.json -| | | └── README.md -| ├── data.js -| ├── index.html -| ├── main.js -| └── style.css -└── test - └── data.spec.js - -directory: 7 file: 20 -``` - -### `src/index.html` - -Como en el proyecto anterior, existe un archivo `index.html`. Como ya sabes, -acá va la página que se mostrará al usuario. También nos sirve para indicar -qué scripts se usarán y unir todo lo que hemos hecho. - -### `src/main.js` - -Recomendamos usar `src/main.js` para todo tu código que tenga que ver con -mostrar los datos en la pantalla. Con esto nos referimos básicamente a la -interacción con el DOM. Operaciones como creación de nodos, registro de -manejadores de eventos (_event listeners_ o _event handlers_), .... - -Esta no es la única forma de dividir tu código, puedes usar más archivos y -carpetas, siempre y cuando la estructura sea clara para tus compañeras. - -En este archivo encontrarás una serie de _imports_ _comentados_. Para _cargar_ -las diferentes fuentes de datos tendrás que _descomentar_ la línea -correspondiente. - -Por ejemplo, si "descomentamos" la siguiente línea: - -```js -// import data from './data/lol/lol.js'; -``` - -La línea quedaría así: - -```js -import data from './data/lol/lol.js'; -``` - -Y ahora tendríamos la variable `data` disponible en el script `src/main.js`. - -### `src/data.js` - -El corazón de este proyecto es la manipulación de datos a través de arreglos -y objetos. - -Te recomendamos que este archivo contenga toda la funcionalidad que corresponda -a obtener, procesar y manipular datos (tus funciones). Por ejemplo: - -* `filterData(data, condition)`: esta función `filter` o filtrar recibiría la - data, y nos retornaría aquellos datos que sí cumplan con la condición. - -* `sortData(data, sortBy, sortOrder)`: esta función `sort` u ordenar - recibe tres parámetros. - El primer parámetro, `data`, nos entrega los datos. - El segundo parámetro, `sortBy`, nos dice con respecto a cuál de los campos de - la data se quiere ordenar. - El tercer parámetro, `sortOrder`, indica si se quiere ordenar de manera - ascendente o descendente. - -* `computeStats(data)`: la función `compute` o calcular, nos permitirá hacer - cálculos estadísticos básicos para ser mostrados de acuerdo a la data - proporcionada. - -Estos nombres de funciones y de parámetros son solamente referenciales, lo que -decidas depende de tu propia implementación. - -Estas funciones deben ser [_puras_](https://medium.com/laboratoria-developers/introducci%C3%B3n-a-la-programaci%C3%B3n-funcional-en-javascript-parte-2-funciones-puras-b99e08c2895d) -e independientes del DOM. Estas funciones serán después usadas desde el archivo -`src/main.js`, al cargar la página, y cada vez que el usuario interactúe (click, -filtrado, ordenado, ...). - -### `src/data` - -En esta carpeta están los datos de las diferentes fuentes. Encontrarás una -carpeta por cada fuente, y dentro de cada carpeta dos archivos: uno con la -extensión `.js` y otro `.json`. Ambos archivos contienen la misma data; la -diferencia es que el `.js` lo usaremos a través de una etiqueta ` - - + + + + Data Ghibli + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +
+
+ + + +
+
+

«It's wonderful when you can bring sparkle into people's lives, especially under difficult circumstances.» +

+

+ Welcome to the most complete Studio Ghibli data! +

+

+ Find your favorite characters, immerse yourself in the magic of movie locations or just enjoy your favorite + soundtrack in Lo-Fi mode. Yes, we have music to accompany the fantastic tour of the best of Studio Ghibli, just + click the "Get Started" button, you can pause the music at any time with the "pause" button. +

+
+ + + +
+
+
+ + + +
+ + + + + +
+ +
+
+ + +
+
+ +
+
+ + +
+
+ + + +
+
+ +
+
+ portada +
+
+

Howl's Moving Castle

+ Description: +

+ When Sophie, a shy young woman, is cursed with an old body by a spiteful witch, her only chance of + breaking the spell lies with a self-indulgent yet insecure young wizard and his companions in his legged, + walking home. +

+
+
+ Director: +

Hayao Miyazaki

+
+
+ Producer: +

Toshio Suzuki

+
+
+ Release Date: +

2004

+
+
+ Score: +

87

+
+
+
+ + +
+
+ + +
+
+
+
+ Personaje 1 +
+
+
+

Howl Jenkins Pendragon

+
+
+
+ Gender +

Male

+
+
+ Age +

27

+
+
+ Eye color +

Bright blue

+
+
+ Hair color +

Bright blue

+
+
+ Species +

Wizard

+
+
+
+
+
+
+ Personaje 1 +
+
+
+

Sophie Hatter

+
+
+
+ Gender +

Female

+
+
+ Age +

18

+
+
+ Eye color +

Brown

+
+
+ Hair color +

white

+
+
+ Species +

Human

+
+
+
+
+
+
+ Personaje 1 +
+
+
+

Calcifer

+
+
+
+ Gender +

Male

+
+
+ Age +

NA

+
+
+ Eye color +

Dark Brown

+
+
+ Hair color +

Orange-yellow

+
+
+ Species +

Demon

+
+
+
+
+
+
+
+ + +
+ +
+ + + + + + + + + \ No newline at end of file diff --git a/src/main.js b/src/main.js index 71c59f2d..2ede8bea 100644 --- a/src/main.js +++ b/src/main.js @@ -1,6 +1,796 @@ -import { example } from './data.js'; -// import data from './data/lol/lol.js'; -import data from './data/pokemon/pokemon.js'; -// import data from './data/rickandmorty/rickandmorty.js'; +/* + ! DESCRIPCIÓN + * Main.js contiene la información asociada a los botones, uso del DOM y display de tarjetas + */ -console.log(example, data); +// ! IMPORTACIONES +import { obtenerPeliculas,obtenerPeliculaPorId,obtenerDirectores, obtenerProductores, aplicarFiltros, buscarTermino, ordenarPeliculasPorAñoAscendente, ordenarPeliculasPorAñoDescendente, ordenarPeliculasAZ, ordenarPeliculasZA,calcularPromedioScore } from "./data.js"; +import data from "./data/ghibli/ghibli.js" +// ! REFERENCIAS AL DOM +// * Sección buscar +const btnBuscar = document.querySelector("#buscar"); +const inputBuscar = document.querySelector("#input-buscar"); + +// * Super contenedor tarjeta +const contenedorDelContenedorDeTarjetas = document.querySelector("#contenedor-del-contenedor-tarjetas") + +// * Música +const botonReproducir = document.querySelector("#reproducir"); +const botonPausar = document.querySelector("#pausar"); +const audio = document.querySelector("#audio"); + +// * Filtros +const peliculasEncontradas = document.querySelector("#peliculas-encontradas"); +const inputMinimo = document.querySelector("#valor-minimo"); +const inputMaximo = document.querySelector("#valor-maximo"); +const selectDirector = document.querySelector("#filtro-director"); +const selectProductor = document.querySelector("#filtro-productor"); +const botonFiltrar = document.querySelector("#btn-filtrar"); +const botonLimpiarFiltros = document.querySelector("#btn-limpiar-filtros"); + +// * Ordenar +const ordenarPorAño = document.querySelector("#ordenar-fecha"); +const ordenarPorAlfabeto = document.querySelector("#ordenar-alfabeticamente"); +const botonPromedio = document.querySelector("#calculo-promedio"); + +// * Comentado porque nunca fue usado: +// // * Sección personajes +// const tarjetaPelicula = document.querySelector(".tarjeta-pelicula"); +// const contenedorTarjeta = document.querySelector(".contenedor-tarjeta"); + + + +// ! FUNCIONES +// * Función total de películas +const peliculas = obtenerPeliculas(data); + +// * Resultados +let resultados = [...obtenerPeliculas(data)]; + +//* Función total directores +const directores = obtenerDirectores(data); + +// * Función total productores +const productores = obtenerProductores(data); + +// * Función reproducir música +botonReproducir.addEventListener("click", () => { + audio.play(); + window.scrollTo(0, 1000); +}) + +// * Función pausar música +botonPausar.addEventListener("click", () => { + audio.pause(); +}); + +// * Función modular el volumen del audio +audio.volume = 0.5; + +// * Función mostrar tarjetas +const mostrarPeliculas = (peliculas) => { + if (peliculas.length === 0){ + peliculasEncontradas.textContent = "0 movies found"; + contenedorDelContenedorDeTarjetas.innerHTML = "" + // Article no se encontraron películas + const noSeEncontraronPeliculas = document.createElement("article"); + noSeEncontraronPeliculas.classList.add("no-se-encontraron-peliculas"); + + // Imagen del indicativo + const imagenSonrisaTotoro = document.createElement("img"); + imagenSonrisaTotoro.src = "img/no-movies-found-totoro.gif"; + + // Texto del indicativo + const textoNoSeEncontraronPeliculas = document.createElement("h2"); + textoNoSeEncontraronPeliculas.textContent = "No Movies Found" + + // Añadir hijos al Article + noSeEncontraronPeliculas.appendChild(imagenSonrisaTotoro); + noSeEncontraronPeliculas.appendChild(textoNoSeEncontraronPeliculas); + + // Añadiendo el Article a la sección + contenedorDelContenedorDeTarjetas.appendChild(noSeEncontraronPeliculas); + + } else { + contenedorDelContenedorDeTarjetas.innerHTML = ""; + peliculas.forEach(pelicula => { + // Mostrar la cantidad de películas encontradas + peliculasEncontradas.textContent = peliculas.length + " movies found" + + // Crear el Div contenedor donde estará la tarjeta + const contenedorTarjeta = document.createElement("div"); + contenedorTarjeta.classList.add("contenedor-tarjeta"); + + // Article de la tarjeta + const tarjetaPelicula = document.createElement("article"); + tarjetaPelicula.classList.add("tarjeta-pelicula"); + + // Div contenedor imagen + const contenedorImagen = document.createElement("div"); + contenedorImagen.classList.add("contenedor-img"); + // Añadiendo el Div contenedor imagen a la tarjeta + tarjetaPelicula.appendChild(contenedorImagen) + + // Imagen portada + const imagen = document.createElement("img"); + imagen.src = pelicula.poster; + imagen.alt = "portada-película"; + imagen.id = "portada"; + contenedorImagen.appendChild(imagen); + + // ! Div contenedor información + const contenedorInfo = document.createElement("div"); + contenedorInfo.classList.add("contenedor-info"); + tarjetaPelicula.appendChild(contenedorInfo); + + // * Elementos dentro del contenedorInfo + // Título película + const titulo = document.createElement("h3"); + titulo.id = "titulo"; + titulo.textContent = pelicula.title; + contenedorInfo.appendChild(titulo); + // Descripción span y p + const descripcionSpan = document.createElement("span"); + descripcionSpan.id = "descripcion"; + descripcionSpan.textContent = "Description"; + contenedorInfo.appendChild(descripcionSpan); + + const descripcion = document.createElement("p"); + descripcion.id = "descripcion"; + descripcion.textContent = pelicula.description; + contenedorInfo.appendChild(descripcion); + + // ! Div director, productor, fecha y score + const infoDiv = document.createElement("div"); + infoDiv.classList.add("director-productor-fecha-puntuacion"); + + // * Elementos dentro del div director, productor, fecha y score + // Div director + const directorDiv = document.createElement("div"); + directorDiv.classList.add("info"); + const directorSpan = document.createElement("span"); + directorSpan.textContent = "Director:"; + const directorP = document.createElement("p"); + directorP.id = "director"; + directorP.textContent = pelicula.director; + directorDiv.appendChild(directorSpan); + directorDiv.appendChild(directorP); + infoDiv.appendChild(directorDiv); + + // Div Productor + const producerDiv = document.createElement("div"); + producerDiv.classList.add("info"); + const producerSpan = document.createElement("span"); + producerSpan.textContent = "Producer:"; + const producerP = document.createElement("p"); + producerP.id = "productor"; + producerP.textContent = pelicula.producer; + producerDiv.appendChild(producerSpan); + producerDiv.appendChild(producerP); + infoDiv.appendChild(producerDiv); + + // Div Fecha + const dateDiv = document.createElement("div"); + dateDiv.classList.add("info"); + const dateSpan = document.createElement("span"); + dateSpan.textContent = "Release Date:"; + const dateP = document.createElement("p"); + dateP.id = "fecha"; + dateP.textContent = pelicula.release_date; + dateDiv.appendChild(dateSpan); + dateDiv.appendChild(dateP); + infoDiv.appendChild(dateDiv); + + //Div Score + const scoreDiv = document.createElement("div"); + scoreDiv.classList.add("info"); + const scoreSpan = document.createElement("span"); + scoreSpan.textContent = "Score:"; + const scoreP = document.createElement("p"); + scoreP.id = "puntuacion"; + scoreP.textContent = pelicula.rt_score; + scoreDiv.appendChild(scoreSpan); + scoreDiv.appendChild(scoreP); + infoDiv.appendChild(scoreDiv); + + contenedorInfo.appendChild(infoDiv); + + //Div botones + const botonContenedor = document.createElement("div"); + botonContenedor.classList.add("contenedor-btn"); + + // Boton personajes + const botonPersonajes = document.createElement("button"); + botonPersonajes.classList.add("btn"); + botonPersonajes.classList.add("btn-personajes"); + botonPersonajes.textContent = "Characters"; + botonPersonajes.id="btn-personajes"; + botonPersonajes.setAttribute("data-id", pelicula.id) // para tener contexto y poder generar los personajes + botonContenedor.appendChild(botonPersonajes); + + // Boton Lugares + const botonLugares = document.createElement("button"); + botonLugares.classList.add("btn"); + botonLugares.classList.add("btn-lugares"); + botonLugares.textContent = "Locations & Vehicles"; + botonLugares.id="btn-lugares"; + botonLugares.setAttribute("data-id", pelicula.id) // para tener contexto y poder generar los personajes + botonContenedor.appendChild(botonLugares); + + contenedorInfo.appendChild(botonContenedor); + + // Agregar contenedorinfo dentro de tarjetaPelicula + tarjetaPelicula.appendChild(contenedorInfo); + + // Agregar tarjetaPelicula dentro de contenedorTarjetas + contenedorTarjeta.appendChild(tarjetaPelicula); + + // Agregar contenedorTarjetas dentro de seccionTarjetas + contenedorDelContenedorDeTarjetas.appendChild(contenedorTarjeta); + + }); + } + // obtenerBotonesPersonajes() +}; + +// * Función para obtener botón de personajes: Comentada porque no fue necesaria, fue reemplazada por delegación de eventos +// function obtenerBotonesPersonajes (){ +// const btnPersonajes = document.querySelectorAll(".btn-personajes"); // para poder utilizar todos los botones personaje... +// console.log(btnPersonajes) +// } + +mostrarPeliculas(peliculas); + +//! Delegación de eventos: botones de tarjetas en general +document.addEventListener('click', function(event) { + const elementoClickeado = event.target; + console.log("Clickeaste en:", elementoClickeado); + + // Ver si el elemento es un botón con clase 'btn-personajes' + if (event.target.classList.contains('btn-personajes')) { + // Obtener el valor del atributo data-id del botón + const dataIdValue = event.target.getAttribute('data-id'); + const tarjetaPelicula = event.target.closest('.tarjeta-pelicula'); + tarjetaPelicula.classList.toggle("active-personajes"); + // inicializarCarrusel() + mostrarPersonajes(dataIdValue, tarjetaPelicula); + } + //ver si el elemento es un botón "boton-lugares" + else if (event.target.classList.contains('btn-lugares')) { + + const dataIdValue = event.target.getAttribute('data-id'); + const tarjetaPelicula = event.target.closest('.tarjeta-pelicula'); + tarjetaPelicula.classList.toggle("active-lugares"); + mostrarLugaresVehiculos(dataIdValue, tarjetaPelicula); + } +}); + +// * Función de Calcular Promedio +botonPromedio.addEventListener("click", () => { + const promedioPeliculas = calcularPromedioScore(resultados); + const dialogPromedio = document.createElement("dialog"); + const tituloPromedio = document.createElement("h2"); + tituloPromedio.textContent = "Average Movie Rating" + dialogPromedio.appendChild(tituloPromedio) + + const parrafoPromedio = document.createElement("p"); + parrafoPromedio.textContent = "The average rating per film is " + promedioPeliculas; + dialogPromedio.appendChild(parrafoPromedio); + const botonContenedor = document.createElement("div"); + botonContenedor.classList.add("contenedor-btn"); + + const botonPromedio = document.createElement("button"); + botonPromedio.classList.add("btn"); + botonPromedio.textContent = "Close"; + + botonContenedor.appendChild(botonPromedio); + dialogPromedio.appendChild(botonContenedor); + + contenedorDelContenedorDeTarjetas.appendChild(dialogPromedio); + dialogPromedio.showModal(); + + botonPromedio.addEventListener("click", () => { + dialogPromedio.close(); + }) +}); +// ? Explicación : +//Cuando se hace clic en cualquier parte del documento, se ejecuta una función que verifica si el elemento clickeado es un botón con la clase 'btn-personajes'. Si es así, obtiene el valor del atributo data-id del botón y encuentra el elemento más cercano con la clase 'tarjeta-pelicula'. Luego, alterna la clase 'active' en ese elemento y llama a la función mostrarPersonajes() pasando el valor de data-id y el elemento tarjetaPelicula como argumentos. + +// * Funciones Ordenar: +// Por Año +// eventListener para un select. y +// ! default sorting no tiene funcionalidad +ordenarPorAño.addEventListener("change", () => { + // const peliculas = obtenerPeliculas(); + // Necesitamos que use una función para una opción y la otra para la otra + if (ordenarPorAño.value === "orden-descendente") { + const peliculasOrdenadas= ordenarPeliculasPorAñoDescendente(resultados); + mostrarPeliculas(peliculasOrdenadas); + + } else if (ordenarPorAño.value === "orden-ascendente") { + const peliculasOrdenadas= ordenarPeliculasPorAñoAscendente(resultados); + mostrarPeliculas(peliculasOrdenadas); + } +}); + +// Por Letra +// eventListener para un select. y +// ! default sorting no tiene funcionalidad +ordenarPorAlfabeto.addEventListener("change", () => { + // const peliculas = obtenerPeliculas(); + // Necesitamos que use una función para una opción y la otra para la otra + if (ordenarPorAlfabeto.value === "ordenar-a-z") { + const peliculasOrdenadas= ordenarPeliculasAZ(resultados); + mostrarPeliculas(peliculasOrdenadas); + + } else if (ordenarPorAlfabeto.value === "ordenar-z-a") { + const peliculasOrdenadas= ordenarPeliculasZA(resultados); + mostrarPeliculas(peliculasOrdenadas); + } +}); + +// * Función para buscar películas +btnBuscar.addEventListener("click", () => { + const peliculasEncontradas = buscarTermino(inputBuscar.value,data); + resultados = [...peliculasEncontradas]; + mostrarPeliculas(resultados); + // Vaciar el input para buscar nuevamente + inputBuscar.value = ""; + window.scrollTo(0, 1000); +}); + +// * Función mostrar directores +directores.forEach(director => { + // Option director + const opcionDirector = document.createElement('option') + opcionDirector.textContent = director; + + // Añadiendo optionDirector al select + selectDirector.appendChild(opcionDirector); +}); + +// * Función mostrar productores +productores.forEach(productor => { + // Option productor + const opcionProductor = document.createElement('option'); + opcionProductor.textContent = productor; + + // Añadiendo optionProductor al select + selectProductor.appendChild(opcionProductor); +}); + +// * Función filtros +botonFiltrar.addEventListener("click", () => { + const resultadoFiltro = aplicarFiltros(inputMinimo.value,inputMaximo.value, selectDirector.value, selectProductor.value, resultados); + + resultados = [...resultadoFiltro]; + mostrarPeliculas(resultados); +}); + +// * Función para limpiar filtros +botonLimpiarFiltros.addEventListener("click", () => { + inputMinimo.value = ""; + inputMaximo.value = ""; + selectDirector.selectedIndex = 0; + selectProductor.selectedIndex = 0; + mostrarPeliculas(peliculas); +}); + +// * Lo siguiente se encuentra comentado pues esta función fue agregada DENTRO de la función mostrar personajes +// // * Función cerrar el overlay si se hace clic fuera del contenedor +// contenedorTarjeta.addEventListener("click", (event) => { +// if (event.target.classList.contains('fa-circle-xmark')) { +// tarjetaPelicula.classList.remove("active"); +// } +// }); + + +//? Sección Overlays: personajes, vehículos y lugares +// * Función mostrar personajes +// Agregar botón para cerrar el overlay. +const mostrarPersonajes = (peliculaId, tarjetaPelicula) => { + + const pelicula = obtenerPeliculaPorId(data, peliculaId) + //* Agregar elementos HTML + // Crear el contenedor de overlay + const overlayContenedor = document.createElement("article"); + overlayContenedor.classList.add("overlay-contenedor-personajes"); + + // Agregar botón de anterior + const botonAnterior = document.createElement("i"); + botonAnterior.classList.add("fa-solid", "fa-angle-left"); + botonAnterior.id = "boton-anterior"; + overlayContenedor.appendChild(botonAnterior); + + // Crear el carrusel de personajes + const carruselPersonajes = document.createElement("div"); + carruselPersonajes.classList.add("carrusel-personajes"); + overlayContenedor.appendChild(carruselPersonajes); + + + pelicula.people.forEach(personaje => { + // Crear la tarjeta de personaje + const tarjetaPersonaje = document.createElement("div"); + tarjetaPersonaje.classList.add("tarjeta-personaje"); + + // Crear el contenedor de la imagen + const contenedorImg = document.createElement("div"); + contenedorImg.classList.add("contenedor-img-personaje"); + + // Crear la imagen + const imgPersonaje = document.createElement("img"); + imgPersonaje.src = personaje.img; + imgPersonaje.alt = personaje.name; + + // Agregar la imagen al contenedor de imagen + contenedorImg.appendChild(imgPersonaje); + + // Crear el contenedor de información del personaje + const contenedorInfoPersonaje = document.createElement("div"); + contenedorInfoPersonaje.classList.add("contenedor-info-personaje"); + + // Crear el contenedor del nombre + const contenedorNombre = document.createElement("div"); + contenedorNombre.classList.add("contenedor-nombre"); + + // Crear el elemento h3 del nombre + const nombrePersonaje = document.createElement("h3"); + nombrePersonaje.id = "nombre"; + nombrePersonaje.textContent = personaje.name; + + // Agregar el nombre al contenedor del nombre + contenedorNombre.appendChild(nombrePersonaje); + + // Crear el contenedor de información del personaje + const infoPersonaje = document.createElement("div"); + infoPersonaje.classList.add("info-personaje"); + + // Crear y configurar los elementos de información + const infoElements = [ + { label: "Gender", value: personaje.gender }, + { label: "Age", value: personaje.age }, + { label: "Eye color", value: personaje.eye_color }, + { label: "Hair color", value: personaje.hair_color }, + { label: "Species", value: personaje.specie } + ]; + + infoElements.forEach(info => { + const infoDiv = document.createElement("div"); + infoDiv.classList.add("info"); + + const infoSpan = document.createElement("span"); + infoSpan.textContent = info.label; + + const infoP = document.createElement("p"); + infoP.textContent = info.value; + + infoDiv.appendChild(infoSpan); + infoDiv.appendChild(infoP); + infoPersonaje.appendChild(infoDiv); + }); + + // Agregar los elementos creados a la tarjeta de personaje + tarjetaPersonaje.appendChild(contenedorImg); + tarjetaPersonaje.appendChild(contenedorInfoPersonaje); + contenedorInfoPersonaje.appendChild(contenedorNombre); + contenedorInfoPersonaje.appendChild(infoPersonaje); + + // Agregar la tarjeta de personaje al carrusel + carruselPersonajes.appendChild(tarjetaPersonaje); + }); + + //Agregar botón para salir del overlay + const botonSalir = document.createElement("i"); + botonSalir.classList.add("fa-solid", "fa-circle-xmark"); + botonSalir.id= "boton-salir"; + overlayContenedor.appendChild(botonSalir); + + // Agregar botón de siguiente + const botonSiguiente = document.createElement("i"); + botonSiguiente.classList.add("fa-solid", "fa-angle-right"); + botonSiguiente.id = "boton-siguiente"; + overlayContenedor.appendChild(botonSiguiente); + + + // Agregar elementos al contenedor de tarjetas + tarjetaPelicula.appendChild(overlayContenedor); + + botonSiguiente.addEventListener("click", function () { + const tarjetasPersonajes = carruselPersonajes.querySelectorAll( + ".tarjeta-personaje" + ); + const arrayTarjetas = [...tarjetasPersonajes]; + + const siguiente = arrayTarjetas.shift(); + arrayTarjetas.push(siguiente); + + arrayTarjetas.forEach((x) => { + carruselPersonajes.appendChild(x); + }); + + cambiaPagina(arrayTarjetas); + // Función para cambiar la página del carrusel + function cambiaPagina(array) { + const posicionActual = 0; + const siguientePos = 1; + + for (let i = 0; i < array.length; i++) { + array[i].style.display = "none"; + } + + array[posicionActual].style.display = "flex"; + array[siguientePos].style.display = "flex"; + } + }); + + botonAnterior.addEventListener("click", function () { + const tarjetasPersonajes = carruselPersonajes.querySelectorAll( + ".tarjeta-personaje" + ); + const arrayTarjetas = [...tarjetasPersonajes]; + + const atras = arrayTarjetas.pop(); + arrayTarjetas.unshift(atras); + + arrayTarjetas.forEach((x) => { + carruselPersonajes.appendChild(x); + }); + + cambiaPagina(arrayTarjetas); + cambiaPagina(arrayTarjetas); + // Función para cambiar la página del carrusel + function cambiaPagina(array) { + const posicionActual = 0; + const siguientePos = 1; + + for (let i = 0; i < array.length; i++) { + array[i].style.display = "none"; + } + + array[posicionActual].style.display = "flex"; + array[siguientePos].style.display = "flex"; + } + }); + + botonSalir.addEventListener("click", () => { + tarjetaPelicula.classList.remove("active-personajes"); + }) +}; + +// para eso debo hacer una función (dataPersonajes()) +// que sea capaz de extraer la información de personajes de la película específica +// Luego generar los Divs según la maqueta. +// Mi duda era sobre cómo ibamos a generar la tarjeta de personajes por película, pero +// si utilizamos la información (ID de película) sería facil! //* Implementadisimo y funcional! + +// * Función generar Lugares y vehículos +const mostrarLugaresVehiculos = (peliculaID, tarjetaPelicula) =>{ + // primero debemos generar el pool de info para ponerla en display + const pelicula = obtenerPeliculaPorId(data,peliculaID); + // el ID está en el botón + //Luego creamos el Overlay + const overlayContenedor = document.createElement("article"); + overlayContenedor.classList.add("overlay-contenedor-lugares-vehiculos"); + + // botón anterior + const botonAnterior = document.createElement("i"); + botonAnterior.classList.add("fa-solid", "fa-angle-left"); + botonAnterior.id = "boton-anterior"; + overlayContenedor.appendChild(botonAnterior); + + // Div Carrusel + const carruselLugaresVehiculos = document.createElement("div"); + carruselLugaresVehiculos.classList.add("carrusel-lugares-vehiculos"); + overlayContenedor.appendChild(carruselLugaresVehiculos); + + //Para Lugares hacemos una función que revise cada lugar y le de estructura + pelicula.locations.forEach(location=>{ + const tarjeta = document.createElement("div"); + tarjeta.className = "tarjeta-lugares-vehiculos"; + + const contenedorImg = document.createElement("div"); + contenedorImg.className = "contenedor-img"; + + const imagen = document.createElement("img"); + imagen.src = location.img; + imagen.alt = location.name; + + contenedorImg.appendChild(imagen); + tarjeta.appendChild(contenedorImg); + + const contenedorInfo = document.createElement("div"); + contenedorInfo.className = "contenedor-info-lugares-vehiculos"; + + const contenedorCategoria = document.createElement("div"); + contenedorCategoria.className = "contenedor-categoria"; + + const categoria = document.createElement("h3"); + categoria.className = "categoria"; + categoria.textContent = "Location"; + + contenedorCategoria.appendChild(categoria); + contenedorInfo.appendChild(contenedorCategoria); + + const contenedorNombre = document.createElement("div"); + contenedorNombre.className = "contenedor-nombre"; + + const nombre = document.createElement("h3"); + nombre.className = "nombre"; + nombre.textContent = location.name; + + contenedorNombre.appendChild(nombre); + contenedorInfo.appendChild(contenedorNombre); + + const infoLocation = document.createElement("div"); + infoLocation.className = "info-location"; + + const propiedades = ["Climate", "Terrain", "Surface Water", "Residents"]; + + propiedades.forEach((propiedad) => { + const info = document.createElement("div"); + info.className = "info"; + + const span = document.createElement("span"); + span.textContent = propiedad; + + const p = document.createElement("p"); + p.textContent = location[propiedad.toLowerCase()]; + + info.appendChild(span); + info.appendChild(p); + infoLocation.appendChild(info); + }); + + contenedorInfo.appendChild(infoLocation); + tarjeta.appendChild(contenedorInfo); + + carruselLugaresVehiculos.appendChild(tarjeta); + }); + + //Para Vehiculos usamos otra función que se ajuste al contenido de los Vehiculos + pelicula.vehicles.forEach((vehicle) => { + const tarjeta = document.createElement("div"); + tarjeta.className = "tarjeta-lugares-vehiculos"; + + const contenedorImg = document.createElement("div"); + contenedorImg.className = "contenedor-img"; + + const imagen = document.createElement("img"); + imagen.src = vehicle.img; + imagen.alt = vehicle.name; + + contenedorImg.appendChild(imagen); + tarjeta.appendChild(contenedorImg); + + const contenedorInfo = document.createElement("div"); + contenedorInfo.className = "contenedor-info-lugares-vehiculos"; + + const contenedorCategoria = document.createElement("div"); + contenedorCategoria.className = "contenedor-categoria"; + + const categoria = document.createElement("h3"); + categoria.className = "categoria"; + categoria.textContent = "Vehicles"; + + contenedorCategoria.appendChild(categoria); + contenedorInfo.appendChild(contenedorCategoria); + + const contenedorNombre = document.createElement("div"); + contenedorNombre.className = "contenedor-nombre"; + + const nombre = document.createElement("h3"); + nombre.className = "nombre"; + nombre.textContent = vehicle.name; + + contenedorNombre.appendChild(nombre); + contenedorInfo.appendChild(contenedorNombre); + + const descripcion = document.createElement("p"); + descripcion.textContent = vehicle.description; + + contenedorInfo.appendChild(descripcion); + + const propiedades = ["Vehicle Class", "Length", "Pilot"]; + + propiedades.forEach((propiedad) => { + const info = document.createElement("div"); + info.className = "info"; + + const span = document.createElement("span"); + span.textContent = propiedad; + + let valor = vehicle[propiedad.toLowerCase()]; + if (propiedad === "Pilot") { + valor = vehicle.pilot.name; + } + + const p = document.createElement("p"); + p.textContent = valor; + + info.appendChild(span); + info.appendChild(p); + contenedorInfo.appendChild(info); + }); + + tarjeta.appendChild(contenedorInfo); + + carruselLugaresVehiculos.appendChild(tarjeta); + }); + + //botón salir + const botonSalir = document.createElement("i"); + botonSalir.classList.add("fa-solid", "fa-circle-xmark"); + botonSalir.id= "boton-salir"; + overlayContenedor.appendChild(botonSalir); + + //botón siguiente + const botonSiguiente = document.createElement("i"); + botonSiguiente.classList.add("fa-solid", "fa-angle-right"); + botonSiguiente.id = "boton-siguiente"; + overlayContenedor.appendChild(botonSiguiente); + + // Agregar elementos a tarjeta peliculas + tarjetaPelicula.appendChild(overlayContenedor); + + // event listeners -> Siguiente + botonSiguiente.addEventListener("click", function () { + const tarjetas = carruselLugaresVehiculos.querySelectorAll( + ".tarjeta-lugares-vehiculos" + ); + const arrayTarjetas = [...tarjetas]; + + const siguiente = arrayTarjetas.shift(); + arrayTarjetas.push(siguiente); + + arrayTarjetas.forEach((x) => { + carruselLugaresVehiculos.appendChild(x); + }); + + cambiaPagina(arrayTarjetas); + // Función para cambiar la página del carrusel + function cambiaPagina(array) { + const posicionActual = 0; + const siguientePos = 1; + + for (let i = 0; i < array.length; i++) { + array[i].style.display = "none"; + } + + array[posicionActual].style.display = "flex"; + array[siguientePos].style.display = "flex"; + } + }); + + botonAnterior.addEventListener("click", function () { + const tarjetas = carruselLugaresVehiculos.querySelectorAll( + ".tarjeta-lugares-vehiculos" + ); + const arrayTarjetas = [...tarjetas]; + + const atras = arrayTarjetas.pop(); + arrayTarjetas.unshift(atras); + + arrayTarjetas.forEach((x) => { + carruselLugaresVehiculos.appendChild(x); + }); + + cambiaPagina(arrayTarjetas); + cambiaPagina(arrayTarjetas); + // Función para cambiar la página del carrusel + function cambiaPagina(array) { + const posicionActual = 0; + const siguientePos = 1; + + for (let i = 0; i < array.length; i++) { + array[i].style.display = "none"; + } + + array[posicionActual].style.display = "flex"; + array[siguientePos].style.display = "flex"; + } + }); + botonSalir.addEventListener("click", () => { + tarjetaPelicula.classList.remove("active-lugares"); + }) + +} diff --git a/src/style.css b/src/style.css deleted file mode 100644 index e69de29b..00000000 diff --git a/src/videos/Demo1-mostrarData.mp4 b/src/videos/Demo1-mostrarData.mp4 new file mode 100644 index 00000000..fda4a9f1 Binary files /dev/null and b/src/videos/Demo1-mostrarData.mp4 differ diff --git a/src/videos/Demo2-buscarData.mp4 b/src/videos/Demo2-buscarData.mp4 new file mode 100644 index 00000000..151f5fd9 Binary files /dev/null and b/src/videos/Demo2-buscarData.mp4 differ diff --git "a/src/videos/Demo3-filtrarPorA\303\261o.mp4" "b/src/videos/Demo3-filtrarPorA\303\261o.mp4" new file mode 100644 index 00000000..1f918946 Binary files /dev/null and "b/src/videos/Demo3-filtrarPorA\303\261o.mp4" differ diff --git a/src/videos/Demo4-filtrarPorDirectorProductor.mp4 b/src/videos/Demo4-filtrarPorDirectorProductor.mp4 new file mode 100644 index 00000000..c693a8d5 Binary files /dev/null and b/src/videos/Demo4-filtrarPorDirectorProductor.mp4 differ diff --git a/src/videos/Demo5-promedioScore.mp4 b/src/videos/Demo5-promedioScore.mp4 new file mode 100644 index 00000000..96acf7f9 Binary files /dev/null and b/src/videos/Demo5-promedioScore.mp4 differ diff --git a/test/data.spec.js b/test/data.spec.js index 09b1f23f..fdaf4e9f 100644 --- a/test/data.spec.js +++ b/test/data.spec.js @@ -1,23 +1,377 @@ -import { example, anotherExample } from '../src/data.js'; +import { obtenerPeliculas, obtenerDirectores, obtenerPersonajes,obtenerLugares,obtenerPeliculaPorId,obtenerProductores,obtenerPeliculasPorTitulo, filtrarPeliculasPorAnho, filtrarPeliculasPorDirector, filtrarPeliculasPorProductor, buscarTermino, ordenarPeliculasPorAñoAscendente,} from '../src/data.js'; +import ghibli from "./mock-ghibli.js"; +// TODO: Datos de prueba: simular objeto con data +//*Data películas: +// +const DATA_PRUEBA_PELICULAS = ghibli +const datosDePrueba = { + films: [ + { + producer: 'Isao Takahata', + }, + { + producer: 'Isao Takahata', + }, + { + producer: 'Hayao Miyazaki', + }, + { + producer: 'Hayao Miyazaki', + }, + { + producer: 'Hayao Miyazaki', + }, + ], +}; -describe('example', () => { - it('is a function', () => { - expect(typeof example).toBe('function'); +// TODO: Tests +//* Test para la función obtenerPeliculas +describe("Función obtenerPeliculas()", () => { + it("Es una función", () => { + expect(typeof obtenerPeliculas).toBe('function'); }); + it ("Devuelve un arreglo", () => { + const resultadoObtenido = obtenerPeliculas(DATA_PRUEBA_PELICULAS); + expect(Array.isArray(resultadoObtenido)).toBe(true); + }) + it("Devuelve un arreglo de objetos", () => { + expect(typeof obtenerPeliculas(DATA_PRUEBA_PELICULAS)[0]).toBe("object"); + }); +}); + +//* Test para obtenerPersonajes +describe("Función obtenerPersonajes", () => { + it("debería devolver un array de personajes", () => { + const personajes = obtenerPersonajes(DATA_PRUEBA_PELICULAS); + expect(Array.isArray(personajes)).toBe(true); + expect(personajes.length).toBeGreaterThan(0); + personajes.forEach((personajeArray) => { + expect(Array.isArray(personajeArray)).toBe(true); + personajeArray.forEach((personaje) => { + expect(personaje).toHaveProperty("name"); + expect(typeof personaje.name).toBe("string"); + }); + }); + }); +}); + +//* Test obtenerDirectores +describe('obtenerDirectores()', () => { + it("Es una función", () => { + expect(typeof obtenerDirectores).toBe('function'); + }); + it ("Devuelve un arreglo", () => { + const resultadoObtenido = obtenerDirectores(DATA_PRUEBA_PELICULAS); + expect(Array.isArray(resultadoObtenido)).toBe(true); + }) +}); +describe("Función obtenerLugares", () => { + test("debería devolver un array de lugares", () => { + const lugares = obtenerLugares(DATA_PRUEBA_PELICULAS); + + expect(Array.isArray(lugares)).toBe(true); + + expect(lugares.length).toBeGreaterThan(0); + lugares.forEach((lugarArray) => { + expect(Array.isArray(lugarArray)).toBe(true); + + lugarArray.forEach((lugar) => { + expect(lugar).toHaveProperty("name"); + expect(typeof lugar.name).toBe("string"); + }); + }); + }); +}); +describe("Función obtenerDirectores", () => { + // Prueba positiva + test("debería devolver un array de directores", () => { + const directores = obtenerDirectores(DATA_PRUEBA_PELICULAS); + + // Verificar que la respuesta es un array + expect(Array.isArray(directores)).toBe(true); + + // Verificar que al menos hay un director + expect(directores.length).toBeGreaterThan(0); + + // Verificar que cada director sea una cadena de texto + directores.forEach((director) => { + expect(typeof director).toBe("string"); + }); + }); + + // Prueba negativa + test("debería devolver un array vacío si no se proporciona data", () => { + const directores = obtenerDirectores(null); // Pasar null o datos inexistentes + + // Verificar que la respuesta es un array vacío + expect(Array.isArray(directores)).toBe(true); + expect(directores.length).toBe(0); + }); +}); - it('returns `example`', () => { - expect(example()).toBe('example'); +describe("Obtener película por ID", () => { + it("Debería devolver la película correspondiente cuando se le proporciona un ID válido", () => { + const idPelicula = "2baf70d1-42bb-4437-b551-e5fed5a87abe"; // Reemplaza con un ID válido de una película existente + const peliculaObtenida = obtenerPeliculaPorId(DATA_PRUEBA_PELICULAS,idPelicula); + // Realiza las afirmaciones para verificar que la película obtenida coincide con la película esperada + // Por ejemplo: + expect(peliculaObtenida.id).toBe(idPelicula); + expect(peliculaObtenida.title).toBe("Castle in the Sky"); + // Agrega más expectativas según tus necesidades + }); + + it("Debería devolver undefined cuando se le proporciona un ID no válido", () => { + const idNoValido = "IDNoExistente"; // Reemplaza con un ID que no exista en tus datos de películas + const peliculaObtenida = obtenerPeliculaPorId(DATA_PRUEBA_PELICULAS,idNoValido); + expect(peliculaObtenida).toBeUndefined(); + }); +}); + +describe('obtenerProductores', () => { + it('Debería devolver una lista de productores sin duplicados', () => { + const productores = obtenerProductores(datosDePrueba); + // La función debería devolver ['Isao Takahata', 'Hayao Miyazaki'] sin duplicados + expect(productores).toEqual(['Isao Takahata', 'Hayao Miyazaki']); + }); + + it('Debería devolver una lista vacía si no hay películas', () => { + const productores = obtenerProductores({ films: [] }); + // La función debería devolver una lista vacía + expect(productores).toEqual([]); + }); + + it('Debería devolver una lista vacía si no se proporcionan datos', () => { + const productores = obtenerProductores(undefined); + // La función debería devolver una lista vacía + expect(productores).toEqual([]); }); }); +describe('obtenerPeliculasPorTitulo()', () => { + it('Debería devolver una película cuando se proporciona un título válido', () => { + const titulo = 'Castle in the Sky'; + const peliculasEncontradas = obtenerPeliculasPorTitulo(DATA_PRUEBA_PELICULAS, titulo); + expect(peliculasEncontradas.length).toBeGreaterThan(0); + const peliculaConTituloCorrecto = peliculasEncontradas.some((pelicula) => + pelicula.title.toLowerCase() === titulo.toLowerCase() + ); + expect(peliculaConTituloCorrecto).toBe(true); + }); + + it('Debería devolver una película cuando se proporciona un título válido (ignorando mayúsculas y minúsculas)', () => { + const titulo = 'my neighbor TOTORO'; + const peliculasEncontradas = obtenerPeliculasPorTitulo(DATA_PRUEBA_PELICULAS, titulo); + + + expect(peliculasEncontradas.length).toBeGreaterThan(0); + + const peliculaCoincidente = peliculasEncontradas.some((pelicula) => + pelicula.title.toLowerCase() === titulo.toLowerCase() + ); + + expect(peliculaCoincidente).toBe(true); + }); + + it('Debería devolver una lista vacía cuando se proporciona un título que no existe', () => { + const titulo = 'Título Inexistente'; + const peliculaEncontrada = obtenerPeliculasPorTitulo(DATA_PRUEBA_PELICULAS, titulo); + expect(peliculaEncontrada).toEqual([]); + }); + + it('Debería devolver una lista vacía cuando no se proporciona ningún título', () => { + const peliculaEncontrada = obtenerPeliculasPorTitulo(DATA_PRUEBA_PELICULAS, ''); + expect(peliculaEncontrada).toEqual([]); + }); + + it('Debería devolver una lista vacía cuando no se proporcionan datos', () => { + const peliculaEncontrada = obtenerPeliculasPorTitulo(null, 'Castle in the Sky'); + + // Asegúrate de que la película encontrada sea una lista vacía (datos no proporcionados) + expect(peliculaEncontrada).toEqual([]); + }); +}); + +describe('filtrarPeliculasPorAnho', () => { + it('Debería devolver "Castle in the Sky" cuando se filtra por el rango de años 1986-1986', () => { + const anho1 = 1986; + const anho2 = 1986; + const peliculasFiltradas = filtrarPeliculasPorAnho(anho1, anho2); + expect(peliculasFiltradas.length).toBe(1); + expect(peliculasFiltradas[0].title).toBe("Castle in the Sky"); + }); + + it('Debería devolver "Castle in the Sky" cuando se filtra por el rango de años 1985-1987', () => { + const anho1 = 1985; + const anho2 = 1987; + const peliculasFiltradas = filtrarPeliculasPorAnho(anho1, anho2); + expect(peliculasFiltradas.length).toBe(1); + expect(peliculasFiltradas[0].title).toBe("Castle in the Sky"); + }); + + it('No debería devolver películas cuando se filtra por un rango que no coincide con ningún año', () => { + const anho1 = 1990; + const anho2 = 1995; + const peliculasFiltradas = filtrarPeliculasPorAnho(anho2, anho1); + expect(peliculasFiltradas.length).toBe(0); + }); + + // Puedes agregar más pruebas aquí según tus necesidades + +}); +describe('filtrarPeliculasPorDirector', () => { + it('debería devolver las películas del director especificado', () => { + const director = 'Hayao Miyazaki'; // El director que deseas probar + const peliculasFiltradas = filtrarPeliculasPorDirector(director, DATA_PRUEBA_PELICULAS.films); + expect(peliculasFiltradas).toHaveLength(3); + expect(peliculasFiltradas[0].title).toBe('Castle in the Sky'); + expect(peliculasFiltradas[1].title).toBe('My Neighbor Totoro'); + expect(peliculasFiltradas[2].title).toBe("Kiki's Delivery Service"); + }); + + it('debería devolver un array vacío si no hay películas del director especificado', () => { + const director = 'Director Inexistente'; + const peliculasFiltradas = filtrarPeliculasPorDirector(director, DATA_PRUEBA_PELICULAS.films); + expect(peliculasFiltradas).toHaveLength(0); + }); +}); + +describe('filtrarPeliculasPorProductor', () => { + const peliculas = DATA_PRUEBA_PELICULAS.films; + + it('debería devolver un arreglo vacío si no se encuentran películas coincidentes', () => { + const result = filtrarPeliculasPorProductor('Productor Inexistente', peliculas); + expect(result).toEqual([]); + }); + + it('debería devolver un arreglo de películas producidas por el productor especificado', () => { + const nombreProductor = 'Isao Takahata'; // Reemplaza con el productor que deseas probar + const result = filtrarPeliculasPorProductor(nombreProductor, peliculas); + + // Verificar que cada película en el resultado tenga el productor correcto + result.forEach((pelicula) => { + expect(pelicula.producer).toBe(nombreProductor); + }); + }); + + it('debería devolver todas las películas si se proporciona una cadena vacía como productor', () => { + const result = filtrarPeliculasPorProductor('', peliculas); + expect(result).toEqual(peliculas); + }); +}); + +describe('buscarTermino', () => { + const peliculas = DATA_PRUEBA_PELICULAS.films; + + it('debería devolver un arreglo vacío si no se encuentran películas coincidentes', () => { + const result = buscarTermino('Término Inexistente', { films: [] }); + expect(result).toEqual([]); + }); + + it('debería devolver un arreglo de películas que coinciden con el término de búsqueda (ignorando mayúsculas y minúsculas)', () => { + const terminoBusqueda = 'castle'; + const result = buscarTermino(terminoBusqueda, { films: peliculas }); + + result.forEach((pelicula) => { + expect(pelicula.title.toLowerCase()).toContain(terminoBusqueda.toLowerCase()); + }); + }); + + it('debería devolver todas las películas si se proporciona una cadena vacía como término de búsqueda', () => { + const result = buscarTermino('', { films: peliculas }); + expect(result).toEqual(peliculas); + }); +}); + +describe('ordenarPeliculasPorAñoAscendente', () => { + it('debería ordenar las películas por año de lanzamiento de manera ascendente', () => { + const peliculas = [ + { + id: '1', + title: 'Película 1', + release_date: '1990', + }, + { + id: '2', + title: 'Película 2', + release_date: '1985', + }, + { + id: '3', + title: 'Película 3', + release_date: '2000', + }, + ]; + + const peliculasOrdenadas = ordenarPeliculasPorAñoAscendente(peliculas); + + // Verificar que las películas estén ordenadas de manera ascendente por año de lanzamiento + expect(peliculasOrdenadas).toEqual([ + { + id: '2', + title: 'Película 2', + release_date: '1985', + }, + { + id: '1', + title: 'Película 1', + release_date: '1990', + }, + { + id: '3', + title: 'Película 3', + release_date: '2000', + }, + ]); + }); + + it('debería mantener el orden si las películas ya están ordenadas por año de lanzamiento', () => { + const peliculas = [ + { + id: '1', + title: 'Película 1', + release_date: '1980', + }, + { + id: '2', + title: 'Película 2', + release_date: '1995', + }, + { + id: '3', + title: 'Película 3', + release_date: '2005', + }, + ]; + + const peliculasOrdenadas = ordenarPeliculasPorAñoAscendente(peliculas); -describe('anotherExample', () => { - it('is a function', () => { - expect(typeof anotherExample).toBe('function'); + // Verificar que las películas mantengan su orden original + expect(peliculasOrdenadas).toEqual(peliculas); }); - it('returns `anotherExample`', () => { - expect(anotherExample()).toBe('OMG'); + it('debería devolver una copia de la lista de películas sin modificar la lista original', () => { + const peliculas = [ + { + id: '1', + title: 'Película 1', + release_date: '1990', + }, + { + id: '2', + title: 'Película 2', + release_date: '1985', + }, + { + id: '3', + title: 'Película 3', + release_date: '2000', + }, + ]; + + const peliculasOrdenadas = ordenarPeliculasPorAñoAscendente(peliculas); + + // Verificar que la lista original no se ha modificado + expect([...peliculas]).toEqual(peliculasOrdenadas); }); }); diff --git a/test/mock-ghibli.js b/test/mock-ghibli.js new file mode 100644 index 00000000..45dbb3bc --- /dev/null +++ b/test/mock-ghibli.js @@ -0,0 +1,481 @@ +//mock-ghibli.js +export default { + "studio": "Studio Ghibli Inc.", + "films": [{ + "id": "2baf70d1-42bb-4437-b551-e5fed5a87abe", + "title": "Castle in the Sky", + "description": "The orphan Sheeta inherited a mysterious crystal that links her to the mythical sky-kingdom of Laputa. With the help of resourceful Pazu and a rollicking band of sky pirates, she makes her way to the ruins of the once-great civilization. Sheeta and Pazu must outwit the evil Muska, who plans to use Laputa's science to make himself ruler of the world.", + "director": "Hayao Miyazaki", + "producer": "Isao Takahata", + "poster": "https://static.wikia.nocookie.net/studio-ghibli/images/c/c1/Castle_in_the_Sky.jpg", + "release_date": "1986", + "rt_score": "95", + "people": [ + { + "id": "fe93adf2-2f3a-4ec4-9f68-5422f1b87c01", + "name": "Pazu", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/8/8b/Pazu.jpg", + "gender": "Male", + "age": "13", + "eye_color": "Black", + "hair_color": "Brown", + "specie": "Human" + }, + { + "id": "598f7048-74ff-41e0-92ef-87dc1ad980a9", + "name": "Lusheeta Toel Ul Laputa", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/c/c3/Sheeta.jpg", + "gender": "Female", + "age": "13", + "eye_color": "Black", + "hair_color": "Black", + "specie": "Human" + }, + { + "id": "3bc0b41e-3569-4d20-ae73-2da329bf0786", + "name": "Dola", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/b/b3/Dola.png", + "gender": "Female", + "age": "60", + "eye_color": "Black", + "hair_color": "Peach", + "specie": "Human" + }, + { + "id": "abe886e7-30c8-4c19-aaa5-d666e60d14de", + "name": "Romska Palo Ul Laputa", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/d/d5/Muska.jpg", + "gender": "Male", + "age": "33", + "eye_color": "Black", + "hair_color": "Brown", + "specie": "Human" + }, + { + "id": "e08880d0-6938-44f3-b179-81947e7873fc", + "name": "Uncle Pom", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/d/de/Uncle_Pom.png", + "gender": "Male", + "age": "Unspecified/Elderly", + "eye_color": "Black", + "hair_color": "White", + "specie": "Human" + }, + { + "id": "5c83c12a-62d5-4e92-8672-33ac76ae1fa0", + "name": "General Muoro", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/1/12/Muoro.jpg", + "gender": "Male", + "age": "Unspecified/Adult", + "eye_color": "Black", + "hair_color": "None", + "specie": "Human" + }, + { + "id": "3f4c408b-0bcc-45a0-bc8b-20ffc67a2ede", + "name": "Duffi", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/0/0a/Duffi.png", + "gender": "Male", + "age": "Unspecified/Adult", + "eye_color": "Dark brown", + "hair_color": "Dark brown", + "specie": "Human" + }, + { + "id": "fcb4a2ac-5e41-4d54-9bba-33068db083ca", + "name": "Louis", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/2/28/Charlies.jpg", + "gender": "Male", + "age": "30", + "eye_color": "Dark brown", + "hair_color": "Dark brown", + "specie": "Human" + }, + { + "id": "2cb76c15-772a-4cb3-9919-3652f56611d0", + "name": "Charles", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/f/f0/Charlie.jpg", + "gender": "Male", + "age": "Unspecified/Adult", + "eye_color": "Dark brown", + "hair_color": "Light brown", + "specie": "Human" + }, + { + "id": "f6f2c477-98aa-4796-b9aa-8209fdeed6b9", + "name": "Henri", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/1/15/Henri.jpg", + "gender": "Male", + "age": "Unspecified/Adult", + "eye_color": "Dark brown", + "hair_color": "Reddish brown", + "specie": "Human" + }, + { + "id": "05d8d01b-0c2f-450e-9c55-aa0daa34838e", + "name": "Motro", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/1/17/Eggman_laputa.jpg", + "gender": "Male", + "age": "Unspecified/Adult", + "eye_color": "Dark brown", + "hair_color": "None", + "specie": "Human" + }, + { + "id": "b22a684f-1819-40c8-94a6-d40c3b5e18eb", + "name": "Okami", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/6/65/Okami.jpg", + "gender": "Female", + "age": "50", + "eye_color": "Dark brown", + "hair_color": "Orange", + "specie": "Human" + }, + { + "id": "40c005ce-3725-4f15-8409-3e1b1b14b583", + "name": "Colonel Muska", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/d/d3/Colonelmuska2bodyguards.JPG", + "gender": "Male", + "age": "33", + "eye_color": "Grey", + "hair_color": "Brown", + "specie": "Human" + } + ], + "locations": [ + { + "id": "6ba60a86-7c74-4ec4-a6f4-7112b5705a2f", + "name": "Gondoa", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/2/25/Thumbnail-8.jpeg", + "climate": "TODO", + "terrain": "TODO", + "surface_water": "40", + "residents": [ + "TODO" + ] + }, + { + "id": "26361a2c-32c6-4bd5-ae9c-8e40e17ae28d", + "name": "Pazu's Mines", + "img": "https://preview.redd.it/fmikehlh8dl41.jpg?auto=webp&s=a997a7d6726fc151a438985899b052d0fd357716", + "climate": "Dry", + "terrain": "Hill", + "surface_water": "0", + "residents": [ + "TODO" + ] + }, + { + "id": "0fafa7a3-64c1-43fe-881b-ecb605c01e09", + "name": "Laputa", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/e/e7/Laputa.png", + "climate": "Continental", + "terrain": "City", + "surface_water": "40", + "residents": [ + "TODO" + ] + }, + { + "id": "0132f7f6-fd52-4ac3-b5df-c96b609f77b6", + "name": "Tedis", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/1/13/Tedis.jpg", + "climate": "Continental", + "terrain": "Hill", + "surface_water": "30", + "residents": [ + "TODO" + ] + } + ], + "vehicles": [ + { + "id": "4e09b023-f650-4747-9ab9-eacf14540cfb", + "name": "Air Destroyer Goliath", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/e/e5/Goliath.png", + "description": "A military airship utilized by the government to access Laputa", + "vehicle_class": "Airship", + "length": "1,000", + "pilot": { + "id": "40c005ce-3725-4f15-8409-3e1b1b14b583", + "name": "Colonel Muska" + } + } + ] + }, + { + "id": "58611129-2dbc-4a81-a72f-77ddfc1b1b49", + "title": "My Neighbor Totoro", + "description": "Two sisters move to the country with their father in order to be closer to their hospitalized mother, and discover the surrounding trees are inhabited by Totoros, magical spirits of the forest. When the youngest runs away from home, the older sister seeks help from the spirits to find her.", + "director": "Hayao Miyazaki", + "producer": "Hayao Miyazaki", + "poster": "https://static.wikia.nocookie.net/studio-ghibli/images/d/db/My_Neighbor_Totoro.jpg", + "release_date": "1988", + "rt_score": "93", + "people": [ + { + "id": "986faac6-67e3-4fb8-a9ee-bad077c2e7fe", + "name": "Satsuki Kusakabe", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/f/f2/Satsuki_Kusakabe.jpg", + "gender": "Female", + "age": "11", + "eye_color": "Dark Brown/Black", + "hair_color": "Dark Brown", + "specie": "Human" + }, + { + "id": "d5df3c04-f355-4038-833c-83bd3502b6b9", + "name": "Mei Kusakabe", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/b/b5/God_Jul_Mei_%C3%B6nskar_fr%C3%A5n_Tomten.jpg", + "gender": "Female", + "age": "4", + "eye_color": "Brown", + "hair_color": "Light Brown", + "specie": "Human" + }, + { + "id": "3031caa8-eb1a-41c6-ab93-dd091b541e11", + "name": "Tatsuo Kusakabe", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/d/d6/Tatsuo_Kusakabe.jpg", + "gender": "Male", + "age": "37", + "eye_color": "Brown", + "hair_color": "Dark Brown", + "specie": "Human" + }, + { + "id": "87b68b97-3774-495b-bf80-495a5f3e672d", + "name": "Yasuko Kusakabe", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/7/7b/Yasuko_Kusakabe.png", + "gender": "Female", + "age": "36", + "eye_color": "Brown", + "hair_color": "Dark Brown", + "specie": "Human" + }, + { + "id": "08ffbce4-7f94-476a-95bc-76d3c3969c19", + "name": "Granny", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/d/d9/Granny.png", + "gender": "Female", + "age": "Elder", + "eye_color": "Black", + "hair_color": "Grey", + "specie": "Human" + }, + { + "id": "0f8ef701-b4c7-4f15-bd15-368c7fe38d0a", + "name": "Kanta Ogaki", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/c/c3/Kanta.jpg", + "gender": "Male", + "age": "11", + "eye_color": "Brown", + "hair_color": "Brown", + "specie": "Human" + }, + { + "id": "d39deecb-2bd0-4770-8b45-485f26e1381f", + "name": "Totoro", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/d/df/Totoro_in_the_rain.png", + "gender": "Male", + "age": "1300", + "eye_color": "Grey", + "hair_color": "Grey", + "specie": "Totoro" + }, + { + "id": "591524bc-04fe-4e60-8d61-2425e42ffb2a", + "name": "Chu Totoro", + "img": "https://www.ghibli.jp/gallery/thumb-totoro019.png", + "gender": "NA", + "age": "", + "eye_color": "Black", + "hair_color": "Blue", + "specie": "Totoro" + }, + { + "id": "c491755a-407d-4d6e-b58a-240ec78b5061", + "name": "Chibi Totoro", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/c/c5/Little_Totoro_spirit_moving.gif", + "gender": "NA", + "age": "", + "eye_color": "Black", + "hair_color": "White", + "specie": "Totoro" + }, + { + "id": "f467e18e-3694-409f-bdb3-be891ade1106", + "name": "Catbus", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/3/30/Catbus.jpg", + "gender": "Male", + "age": "NA", + "eye_color": "Yellow", + "hair_color": "Brown", + "specie": "Cat" + } + ], + "locations": [ + { + "id": "660c8c91-bd92-43db-b475-b2df6ca96fec", + "name": "Kusakabe's House", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/f/f3/Kusakabe_Family_House.jpg", + "climate": "Mild", + "terrain": "River", + "surface_water": "40", + "residents": [ + "TODO" + ] + }, + { + "id": "6fc21b76-78fb-4451-98f7-857e32a23e85", + "name": "Matsugo", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/0/08/Min_Granne_Totoro_v%C3%A4rlden.jpg", + "climate": "Continental", + "terrain": "River", + "surface_water": "60", + "residents": [ + "TODO" + ] + }, + { + "id": "ee897b2a-405e-42b9-bff4-8b51b0f03cab", + "name": "Satsuki's School House", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/d/dd/Satsuki%27s_School_House.jpg", + "climate": "Mild", + "terrain": "River", + "surface_water": "60", + "residents": [ + "TODO" + ] + } + ], + "vehicles": [] + }, + { + "id": "ea660b10-85c4-4ae3-8a5f-41cea3648e3e", + "title": "Kiki's Delivery Service", + "description": "A young witch, on her mandatory year of independent life, finds fitting into a new community difficult while she supports herself by running an air courier service.", + "director": "Hayao Miyazaki", + "producer": "Hayao Miyazaki", + "poster": "https://static.wikia.nocookie.net/studio-ghibli/images/4/48/Kiki%27s_Delivery_Service_%282%29.jpg", + "release_date": "1989", + "rt_score": "96", + "people": [ + { + "id": "4151abc6-1a9e-4e6a-5678-aac05ra641js", + "name": "Kiki", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/5/59/Kiki.jpg", + "gender": "Female", + "age": "13", + "eye_color": "Black", + "hair_color": "Brown", + "specie": "Witch" + }, + { + "id": "7151abc6-1a9e-4e6a-9711-ddb50ea572ec", + "name": "Jiji", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/b/b4/Jiji.jpg", + "gender": "Male", + "age": "13", + "eye_color": "Black", + "hair_color": "Black", + "specie": "Cat" + }, + { + "id": "6574cfr2-9w3d-2x1h-8531-gge23iu489ko", + "name": "Ursula", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/1/12/Ursula.jpg", + "gender": "Female", + "age": "18", + "eye_color": "Black", + "hair_color": "Red", + "specie": "Human" + }, + { + "id": "4321dse3-6r6s-3r4d-5641-rdq19re765de", + "name": "Tombo", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/6/6f/Tombo.jpg", + "gender": "Male", + "age": "13", + "eye_color": "Black", + "hair_color": "Brown", + "specie": "Human" + }, + { + "id": "8252ebf6-1g8f-5t6u-1234-vvg45yd363dc", + "name": "Osono", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/3/3a/Osono.jpg", + "gender": "Female", + "age": "30", + "eye_color": "Brown", + "hair_color": "Red", + "specie": "Human" + }, + { + "id": "7654ght4-3r4t-1t5u-0987-hhj76gh432gr", + "name": "Fukuo", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/2/29/Fukuo.jpg", + "gender": "Male", + "age": "Adult", + "eye_color": "Black", + "hair_color": "Black", + "specie": "Human" + } + ], + "locations": [ + { + "id": "fb083a4e-77b2-4623-a2e0-6bbca5bfd5b2", + "name": "Ursula's Log Cabin", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/b/b5/Ursula%27s_Log_Cabin.jpg", + "climate": "TODO", + "terrain": "TODO", + "surface_water": "40", + "residents": [ + "TODO" + ] + }, + { + "id": "c57fb2cb-ea85-4d73-8808-cf5dcd28c22e", + "name": "Koriko", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/0/05/Koriko.jpg", + "climate": "Mild", + "terrain": "Hill", + "surface_water": "50", + "residents": [ + "TODO" + ] + }, + { + "id": "62346d33-caa0-4c17-8016-0aca56f3066b", + "name": "Karikiya", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/e/e3/Majo002.jpg", + "climate": "Mild", + "terrain": "City", + "surface_water": "30", + "residents": [ + "TODO" + ] + }, + { + "id": "64a996aa-481e-4627-9624-ab23f59a05a9", + "name": "Guchokipanya", + "img": "https://static.wikia.nocookie.net/studio-ghibli/images/0/0f/Gutiokipanja.jpg", + "climate": "Continental", + "terrain": "Hill", + "surface_water": "50", + "residents": [ + { + "id": "8252ebf6-1g8f-5t6u-1234-vvg45yd363dc", + "name": "Osono" + }, + { + "id": "7654ght4-3r4t-1t5u-0987-hhj76gh432gr", + "name": "Fukuo" + } + ] + } + ], + "vehicles": [] + } + ] +} \ No newline at end of file