diff --git a/src/components/pages/Exercise01/index.js b/src/components/pages/Exercise01/index.js index b36b04f..3e30ef9 100644 --- a/src/components/pages/Exercise01/index.js +++ b/src/components/pages/Exercise01/index.js @@ -1,20 +1,112 @@ /** * Exercise 01: The Retro Movie Store * Implement a shopping cart with the next features for the Movie Store that is selling retro dvds: - * 1. Add a movie to the cart - * 2. Increment or decrement the quantity of movie copies. If quantity is equal to 0, the movie must be removed from the cart - * 3. Calculate and show the total cost of your cart. Ex: Total: $150 + * 1. Add a movie to the cart [done] + * 2. Increment or decrement the quantity of movie copies. If quantity is equal to 0, the movie must be removed from the cart [done] + * 3. Calculate and show the total cost of your cart. Ex: Total: $150 [done] * 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.[done] * * 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 { useState, createContext, useContext } from 'react' -export default function Exercise01 () { +const Context = createContext() + +const SingleMovie = (props) => { + const { movieInfo } = props + const { setCart, cart } = useContext(Context) + const handleAddToCart = () => { + + const cartIdExist = () => { + let exist = false + for (let i = 0; i < cart.length; i++) { + const item = cart[i]; + if (item.id === movieInfo.id) { + exist = true + break + } + } + return exist + } + if (cartIdExist()) { + return + } + movieInfo.quantity = 1 + const cartCopy = [...cart] + cartCopy.push(movieInfo) + setCart(cartCopy) + } + return ( +
  • + + +
  • + ) +} + +const SingleCartItem = ({ cartItem }) => { + const { cart, setCart } = useContext(Context) + const handleCount = (increment) => { + const cartCopy = [...cart] + for (let i = 0; i < cart.length; i++) { + const item = cart[i]; + if (item.id === cartItem.id) { + if (increment) { + item.quantity++ + } else { + item.quantity-- + } + if (item.quantity === 0) { + cartCopy.splice(i, 1) + } + break + } + } + setCart(cartCopy) + } + return
  • + +
    + + + {cartItem.quantity} + + +
    +
  • +} + +export default function Exercise01() { const movies = [ { id: 1, @@ -50,77 +142,59 @@ export default function Exercise01 () { { m: [4, 2], discount: 0.1 - } + } ] - const [cart, setCart] = useState([ - { - id: 1, - name: 'Star Wars', - price: 20, - quantity: 2 + const [cart, setCart] = useState([]) + + const getTotal = () => { + let total = 0 + for (let i = 0; i < cart.length; i++) { + const cartItem = cart[i]; + total += cartItem.price * cartItem.quantity } - ]) - const getTotal = () => 0 // TODO: Implement this + const checkForDiscount = () => { + for (let i = 0; i < discountRules.length; i++) { + const rule = discountRules[i]; + if (rule.m.length !== cart.length) continue + const ruleMatch = rule.m.every((id) => { + return cart.some((cartItem) => { + return cartItem.id === id + }) + }) + if (ruleMatch) { + return rule.discount + } + } + } + const discount = checkForDiscount() + return { total, discount } + } + const { total, discount } = getTotal() return ( -
    -
    - -
    -
    - -
    -

    Total: ${getTotal()}

    + +
    +
    +
      + {movies.map((o, idx) => { + return () + })} +
    +
    +
    +
      + {cart.length === 0 &&

      Cart is empty add at least one item

      } + {cart.map((cartItem, idx) => ( + + ))} +
    +
    +

    Total: {discount && <>From ${total} to }${(total && discount) ? (total - (total * (discount / 100))) : total} {discount && <>With a discount of {discount * 100}% applied}

    +
    -
    -
    + + ) -} \ No newline at end of file +} \ 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..c0084f1 100644 --- a/src/components/pages/Exercise02/assets/styles.css +++ b/src/components/pages/Exercise02/assets/styles.css @@ -3,25 +3,61 @@ color: var(--white); min-height: 100vh; padding: 2em; + background: url("./mountains.jpeg"), linear-gradient(180deg, #1B1B1B00 0%, #1B1B1B 30%, #1B1B1B 100%); + background-position: top; + background-position-y: 100%; + background-repeat: no-repeat; + background-blend-mode: overlay; + background-attachment: fixed; + background-size: cover; } .movie-library__title { font-size: 2em; margin-bottom: 1em; + font-weight: 900; } .movie-library__actions { display: flex; margin-bottom: 1em; + border: var(--ms-green) solid 2px; + border-radius: 5px; + min-height: 41px; +} + +.movie-library__actions * { + font-family: var(--poppins); + font-size: 0.875em; + line-height: 1; + font-weight: 500; } .movie-library__actions select { width: 100%; padding: .25em; + text-decoration: none; + border: none; + color: var(--black); + background-color: var(--white); + outline: none; +} + +.movie-library__actions select:focus { + outline: none; } .movie-library__actions button { white-space: nowrap; + text-decoration: none; + border: none; + color: var(--black); + background-color: var(--ms-green); + outline: none; +} + +.movie-library__actions button:focus { + outline: none; } .movie-library__loading { @@ -34,24 +70,57 @@ .movie-library__list { padding: 1em; - background-color: #222222; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(15em, 1fr)); + gap: 2.5em; + padding-left: 0px; + padding-right: 0px; + margin-top: 3.625em; } .movie-library__card { background-color: #333333; - margin-bottom: 1em; - display: flex; + display: grid; + grid-template-columns: 1fr; align-items: center; + position: relative; + border-radius: 8px; + max-width: 100%; } .movie-library__card img { - width: 10em; + width: 100%; } .movie-library__card ul { - padding: 1em; + position: absolute; + height: 100%; + width: 100%; + display: flex; + flex-direction: column; + justify-content: flex-end; + border-radius: 8px; + background: linear-gradient(0deg, #96d501 0%, rgba(150, 214, 1, 0.51) 24.27%, rgba(151, 215, 0, 0) 100%); } -.movie-library__card li { - line-height: 120%; +.movie-library .content { + padding-left: 1em; + padding-right: 1em; + padding-bottom: 1em; } + + +.movie-library__card ul .title { + font-family: var(--poppins); + font-weight: 900; + font-size: 1.2em; + line-height: 1; + margin-bottom: 8px; +} + +.movie-library__card ul .sub-title{ + font-weight: 500; + font-size: 0.75em; + font-family: var(--poppins); + margin-bottom: 4px; +} \ No newline at end of file diff --git a/src/components/pages/Exercise02/index.js b/src/components/pages/Exercise02/index.js index f66932f..4388e71 100644 --- a/src/components/pages/Exercise02/index.js +++ b/src/components/pages/Exercise02/index.js @@ -3,11 +3,11 @@ * Exercise 02: Movie Library * We are trying to make a movie library for internal users. We are facing some issues by creating this, try to help us following the next steps: * !IMPORTANT: Make sure to run yarn movie-api for this exercise - * 1. We have an issue fetching the list of movies, check why and fix it (handleMovieFetch) - * 2. Create a filter by fetching the list of gender (http://localhost:3001/genres) and then loading - * 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) + * 1. We have an issue fetching the list of movies, check why and fix it (handleMovieFetch) [done] + * 2. Create a filter by fetching the list of gender (http://localhost:3001/genres) and then loading [done] + * list of movies that belong to that gender (Filter all movies). [done] + * 3. Order the movies by year and implement a button that switch between ascending and descending order for the list [done] + * 4. Try to recreate the user interface that comes with the exercise (exercise02.png) [done] * * You can modify all the code, this component isn't well designed intentionally. You can redesign it as you need. */ @@ -15,19 +15,18 @@ import "./assets/styles.css"; import { useEffect, useState } from "react"; -export default function Exercise02 () { - const [movies, setMovies] = useState([]) +const useFetch = (url) => { + const [data, setData] = useState([]) 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') + fetch(url) .then(res => res.json()) .then(json => { - setMovies(json) + setData(json) setLoading(false) }) .catch(() => { @@ -37,18 +36,41 @@ export default function Exercise02 () { useEffect(() => { handleMovieFetch() - }, [handleMovieFetch]) + }, []) + return { data, loading, fetchCount } +} + +export default function Exercise02() { + const { data: movies, loading, fetchCount } = useFetch('http://localhost:3001/movies?_limit=50') + const { data: genres, loading: loadingGenres, fetchCount: genresFetchCount } = useFetch('http://localhost:3001/genres') + const [genre, setGenre] = useState("") + + const [ascending, setAscending] = useState(false) + const sortMovies = (movies) => { + movies.sort((a, b) => { + if (a.year > b.year) { + return 1 + } + if (a.year < b.year) { + return -1 + } + return 0 + }) + return ascending ? movies : movies.reverse() + } return ( -
    +

    Movie Library

    - setGenre(e.target.value)}> + {genres && } + {loadingGenres && ()} + {genres && genres.map(genre => ())} - +
    {loading ? (
    @@ -57,18 +79,21 @@ export default function Exercise02 () {
    ) : ( )}
    diff --git a/src/styles.css b/src/styles.css index 74d16fb..9814d74 100644 --- a/src/styles.css +++ b/src/styles.css @@ -53,6 +53,7 @@ table { --black: #1B1B1B; --white: #F5F5F5; --ms-green: rgb(151, 215, 0); + --poppins: 'Poppins', sans-serif; font-size: 16px; font-family: 'Poppins', sans-serif; } \ No newline at end of file