diff --git a/src/components/pages/Exercise01/assets/styles.css b/src/components/pages/Exercise01/assets/styles.css index cdba0ca..60886f0 100644 --- a/src/components/pages/Exercise01/assets/styles.css +++ b/src/components/pages/Exercise01/assets/styles.css @@ -8,22 +8,26 @@ justify-content: center; } -.movies__list, .movies__cart { - width: 24em; +.movies__list, +.movies__cart { + width: 25em; } .movies__list { background-color: #222222; padding: 1em; - border-radius: .2em; + border-radius: 0.2em; margin-right: 2em; } .movies__list-card { background-color: #333333; margin-bottom: 1rem; - padding: .75em; - border-radius: .2em; + padding: 0.75em; + border-radius: 0.2em; + display: flex; + flex-direction: row; + align-items: center; } .movies__list-card:last-child { @@ -39,23 +43,26 @@ background-color: var(--ms-green); border: none; cursor: pointer; - font-size: .75em; - border-radius: .25em; - margin-top: .75em; - padding: .25em .5em; + font-size: 0.75em; + border-radius: 0.25em; + margin-top: 0.75em; + padding: 0.25em 0.5em; } .movies__cart { background-color: #222222; padding: 1em; - border-radius: .2em; + border-radius: 0.2em; } .movies__cart-card { background-color: #333333; margin-bottom: 1rem; - padding: .75em; - border-radius: .2em; + padding: 0.75em; + border-radius: 0.2em; + display: flex; + flex-direction: row; + align-items: center; } .movies__cart-card:last-child { @@ -70,23 +77,48 @@ .movies__cart-card-quantity { display: flex; align-items: center; - margin-top: .5em; + margin-top: 0.5em; } .movies__cart-card span { - margin: 0 .5em; + margin: 0 0.5em; } .movies__cart-card button { background-color: var(--ms-green); border: none; cursor: pointer; - font-size: .75em; - border-radius: .25em; - padding: .25em .5em; + font-size: 0.75em; + border-radius: 0.25em; + padding: 0.25em 0.5em; } .movies__cart-total { margin-top: 1em; font-size: 1.25em; -} \ No newline at end of file +} + +.movies__list-header { + margin-top: 0.5em; + margin-bottom: 0.5em; + font-size: 2.25em; + display: flex; +} + +.movies__cart-header { + margin-top: 0.5em; + margin-bottom: 0.5em; + font-size: 2.25em; +} + +.movie-poster { + max-width: 100px; + max-height: 100px; +} + +.movie-info { + display: flex; + flex-direction: column; + justify-content: space-between; + margin-left: 10px; +} diff --git a/src/components/pages/Exercise01/index.js b/src/components/pages/Exercise01/index.js index b36b04f..e04bffb 100644 --- a/src/components/pages/Exercise01/index.js +++ b/src/components/pages/Exercise01/index.js @@ -6,121 +6,168 @@ * 3. Calculate and show the total cost of your cart. Ex: Total: $150 * 4. Apply discount rules. You have an array of offers with discounts depending of the combination of movie you have in your cart. * You have to apply all discounts in the rules array (discountRules). - * Ex: If m: [1, 2, 3], it means the discount will be applied to the total when the cart has all that products in only. - * + * Ex: If m:Â [1, 2, 3], it means the discount will be applied to the total when the cart has all that products in only. + * * You can modify all the code, this component isn't well designed intentionally. You can redesign it as you need. */ -import './assets/styles.css' -import { useState } from 'react' +import "./assets/styles.css"; +import { useState } from "react"; -export default function Exercise01 () { +export default function Exercise01() { + const [cart, setCart] = useState([]); const movies = [ { id: 1, - name: 'Star Wars', - price: 20 + title: "Star Wars", + price: 20, + image: + "https://i.pinimg.com/736x/01/f0/be/01f0be4e4c185993e5fc749916d375f1.jpg", }, { id: 2, - name: 'Minions', - price: 25 + title: "Minions", + price: 25, + image: "https://www.ecartelera.com/carteles/14500/14511/002_p.jpg", }, { id: 3, - name: 'Fast and Furious', - price: 10 + title: "Fast and Furious", + price: 10, + image: + "https://archivos-cms.cinecolombia.com/images/1/2/7/8/18721-5-esl-CO/FF9_DIGTAL_1_SHEET_MONTAGE_LAT-AM.jpg", }, { id: 4, - name: 'The Lord of the Rings', - price: 5 - } - ] - - const discountRules = [ - { - m: [3, 2], - discount: 0.25 + title: "The Lord of the Rings", + price: 5, + image: "https://www.ecartelera.com/carteles/2600/2650/001_m.jpg", }, { - m: [2, 4, 1], - discount: 0.5 + id: 5, + title: "Ghostbusters", + price: 6, + image: "https://www.ecartelera.com/carteles/4400/4441/001_m.jpg", }, - { - m: [4, 2], - discount: 0.1 - } - ] + ]; + const discountRules = [ + { movies: [3, 2], discount: 0.25 }, + { movies: [2, 4, 1], discount: 0.5 }, + { movies: [4, 2], discount: 0.1 }, + { movies: [1, 4, 5], discount: 0.2 }, + ]; - const [cart, setCart] = useState([ - { - id: 1, - name: 'Star Wars', - price: 20, - quantity: 2 + const getMovieById = (id) => { + return movies.find((movie) => movie.id === id); + }; + + const addMovieToCart = (id, quantity = 1) => { + const movie = getMovieById(id); + if (movie) { + const cartItem = cart.find((item) => item.movie.id === id); + if (cartItem) { + cartItem.quantity += quantity; + if (cartItem.quantity <= 0) { + setCart(cart.filter((item) => item !== cartItem)); + } else { + setCart([...cart]); + } + } else if (quantity > 0) { + setCart([...cart, { movie, quantity }]); + } } - ]) + }; - const getTotal = () => 0 // TODO: Implement this + const calculateTotalCost = () => { + const total = cart.reduce( + (acc, item) => acc + item.movie.price * item.quantity, + 0 + ); + const discount = calculateDiscount(); + return total - discount; + }; + + const calculateDiscount = () => { + let maxDiscount = 0; + discountRules.forEach((rule) => { + const hasAllMovies = rule.movies.every((movieId) => + cart.some((item) => item.movie.id === movieId) + ); + if (hasAllMovies && rule.discount > maxDiscount) { + maxDiscount = rule.discount; + } + }); + return cart.reduce( + (acc, item) => acc + item.movie.price * item.quantity * maxDiscount, + 0 + ); + }; return (
+
+

Movies Catalog

+
+ ))}
))}
-

Total: ${getTotal()}

+

Total: ${calculateTotalCost()}

- ) -} \ No newline at end of file + ); +} diff --git a/src/components/pages/Exercise02/assets/styles.css b/src/components/pages/Exercise02/assets/styles.css index 39e82de..83d4188 100644 --- a/src/components/pages/Exercise02/assets/styles.css +++ b/src/components/pages/Exercise02/assets/styles.css @@ -1,27 +1,62 @@ .movie-library { background-color: var(--black); color: var(--white); - min-height: 100vh; - padding: 2em; + align-items: center; + position: relative; + width: 100%; +} +.movie-library__title-font { + width: 100%; + background-image: url("../assets/mountains.jpeg"); + background-size: cover; + overflow: hidden; +} +.movie-library_container { + width: 100%; + height: 100%; + margin: 5%; } .movie-library__title { - font-size: 2em; + margin-top: 1em; + font-size: 2.5em; + font-weight: bold; margin-bottom: 1em; } .movie-library__actions { display: flex; margin-bottom: 1em; + width: 90%; + border: 1px solid rgb(36, 255, 7); + align-items: center; + border-radius: 10px; + justify-content: center; } .movie-library__actions select { width: 100%; - padding: .25em; + padding: 1%; + border-top-left-radius: 10px; + border-bottom-left-radius: 10px; + min-height: 55px; + max-height: 55px; } +.movie-library__actions text { + background-color: #222222; +} .movie-library__actions button { - white-space: nowrap; + align-items: center; + min-height: 55px; + max-height: 55px; + text-align: center; + font-weight: bold; + display: flex; + border-top-right-radius: 10px; + border-bottom-right-radius: 10px; + background-color: rgb(150, 255, 53); + width: 9rem; } .movie-library__loading { @@ -33,25 +68,45 @@ } .movie-library__list { - padding: 1em; - background-color: #222222; + display: flex; + flex-wrap: wrap; + gap: 3rem; + list-style: none; + justify-content: space-around; + margin-left: 50px; + margin-right: 50px; } .movie-library__card { - background-color: #333333; - margin-bottom: 1em; + width: 20%; + border-radius: 10px; display: flex; - align-items: center; + overflow: hidden; + height: 40rem%; + flex-direction: column; + position: relative; + background: linear-gradient(to top, rgba(136, 255, 0, 0.637), #ffffff00); } .movie-library__card img { - width: 10em; + height: 100%; + object-fit: cover; + width: 100%; + opacity: 0.5; } .movie-library__card ul { - padding: 1em; + position: absolute; + flex-grow: 1; + list-style: none; + margin: 1rem; + height: 100%; + top: 80%; } .movie-library__card li { - line-height: 120%; + margin-bottom: 0.5rem; +} +.movie-library__list_title { + font-weight: bold; } diff --git a/src/components/pages/Exercise02/index.js b/src/components/pages/Exercise02/index.js index f66932f..5d1e36a 100644 --- a/src/components/pages/Exercise02/index.js +++ b/src/components/pages/Exercise02/index.js @@ -8,47 +8,111 @@ * list of movies that belong to that gender (Filter all movies). * 3. Order the movies by year and implement a button that switch between ascending and descending order for the list * 4. Try to recreate the user interface that comes with the exercise (exercise02.png) - * + * * You can modify all the code, this component isn't well designed intentionally. You can redesign it as you need. */ import "./assets/styles.css"; import { useEffect, useState } from "react"; -export default function Exercise02 () { - const [movies, setMovies] = useState([]) - const [fetchCount, setFetchCount] = useState(0) - const [loading, setLoading] = useState(false) +export default function Exercise02() { + const [movies, setMovies] = useState([]); + const [genres, setGenres] = useState([]); + const [selectedGenre, setSelectedGenre] = useState(""); + const [ascendingOrder, setAscendingOrder] = useState(true); + const [fetchCount, setFetchCount] = useState(0); + const [loading, setLoading] = useState(false); const handleMovieFetch = () => { - setLoading(true) - setFetchCount(fetchCount + 1) - console.log('Getting movies') - fetch('http://localhost:3001/movies?_limit=50') - .then(res => res.json()) - .then(json => { - setMovies(json) - setLoading(false) + setLoading(true); + setFetchCount(fetchCount + 1); + console.log("Getting movies"); + fetch("http://localhost:3001/movies?_limit=50") + .then((res) => res.json()) + .then((json) => { + setMovies(json); + setLoading(false); }) .catch(() => { - console.log('Run yarn movie-api for fake api') + console.log("Run yarn movie-api for fake api"); + }); + }; + + const handleGenreFetch = () => { + fetch("http://localhost:3001/genres") + .then((res) => res.json()) + .then((json) => { + setGenres(json); }) - } + .catch(() => { + console.log("Run yarn movie-api for fake api"); + }); + }; + + const handleGenreChange = (event) => { + const selectedValue = event.target.value; + setSelectedGenre(selectedValue); + if (selectedValue === null) { + handleMovieFetch(); + } else { + fetch( + `http://localhost:3001/movies?_limit=50&genres_like=${selectedValue}` + ) + .then((res) => res.json()) + .then((json) => { + setMovies(json); + }) + .catch(() => { + console.log("Run yarn movie-api for fake api"); + }); + } + }; + + const handleOrderChange = () => { + setAscendingOrder(!ascendingOrder); + }; useEffect(() => { - handleMovieFetch() - }, [handleMovieFetch]) + handleMovieFetch(); + handleGenreFetch(); + }, []); + + useEffect(() => { + let sortedMovies = [...movies]; + if (ascendingOrder) { + sortedMovies.sort((a, b) => a.year - b.year); + } else { + sortedMovies.sort((a, b) => b.year - a.year); + } + setMovies(sortedMovies); + }, [ascendingOrder]); return (
-

- Movie Library -

-
- - +
+
+

Movie Library

+ +
+ + +
+
{loading ? (
@@ -57,20 +121,18 @@ export default function Exercise02 () {
) : ( )}
- ) -} \ No newline at end of file + ); +}