diff --git a/cypress/e2e/error_spec.cy.js b/cypress/e2e/error_spec.cy.js index 022bbf2..597fdcb 100644 --- a/cypress/e2e/error_spec.cy.js +++ b/cypress/e2e/error_spec.cy.js @@ -1,26 +1,6 @@ describe('error page', () => { it('should display an error page if failed to get fact', () => { - cy.intercept('https://catfact.ninja/fact', { - statusCode: 500 - }) - cy.visit('http://localhost:3000/') - cy.get('button[class=fact-btn]').click() - cy.get('h2').contains("Looks like something is wrong. Please try again later.") + cy.visit('http://localhost:3000/factsss') + cy.get('.error-message').contains("Looks like something is wrong. Please try again later.") }) - - it('should display message if no favorited facts', () => { - cy.visit('https://cats-meow.vercel.app/') - cy.get('button[class=fav-btn]').click() - cy.get('h2').contains('No saved ideas yet') - }) - it('should be able to navigate back to home page', () => { - cy.intercept('https://catfact.ninja/fact', { - statusCode: 500 - }) - cy.visit('http://localhost:3000/') - cy.get('button[class=fact-btn]').click() - cy.get('h2').contains("Looks like something is wrong. Please try again later.") - cy.get('h3').click() - cy.get('h1').contains('Cats Meow') - }) }) \ No newline at end of file diff --git a/cypress/e2e/fact_spec.cy.js b/cypress/e2e/fact_spec.cy.js index e7e14e1..cd62715 100644 --- a/cypress/e2e/fact_spec.cy.js +++ b/cypress/e2e/fact_spec.cy.js @@ -1,47 +1,26 @@ describe('visiting the fact page', () => { it('should show a random fact upon visit', () => { - cy.clearAllSessionStorage() - cy.intercept('https://catfact.ninja/fact', { + cy.intercept('https://api.thecatapi.com/v1/images/search?size=med&mime_types=jpg&format=json&has_breeds=true&order=RANDOM&page=0&limit=10', { status: 200, fixture: 'cat-facts' }) cy.visit('http://localhost:3000/') cy.get('button[class=fact-btn]').click() - cy.get('p').contains('Cats can see like wicked good') + cy.get('p[class=name]').contains('Siamese') + cy.get('p[class=dog-friendly]').contains("Yes") + cy.get('p[class=energy-level]').contains(5) + cy.get('p[class=affection-level]').contains(5) + cy.get('p[class=origin]').contains('Thailand') + cy.get('p[class=temperament]').contains("Active, Agile, Clever, Sociable, Loving, Energetic") + cy.get('p[class=fact]').contains("While Siamese cats are extremely fond of their people, they will follow you around and supervise your every move, being talkative and opinionated. They are a demanding and social cat, that do not like being left alone for long periods.") + cy.get('img[class=fact-img]').should('be.visible') }) - it('should have two buttons', () => { - cy.clearAllSessionStorage() + it('should have three buttons', () => { cy.visit('http://localhost:3000/fact') - cy.get('button[class=fav-card-btn]').should('be.visible') - cy.get('button[class=new-fact-btn]').should('be.visible') - }) - - it('should display error when failed to fetch fact and provide a path back home', () => { - cy.visit('http://localhost:3000/fact') - cy.intercept('https://catfact.ninja/fact', { - statusCode: 404, - body: '404 Not Found!', - headers: { - 'x-not-found': 'true', - }, - }) - cy.get('h2').contains('Looks like something is wrong. Please try again later.') - cy.get('a').click() - cy.get('h1').contains('Cats Meow') - }) - - it('should maintain fact display over page refresh', () => { - cy.clearAllSessionStorage() - cy.visit('http://localhost:3000/fact') - cy.intercept('https://catfact.ninja/fact', { - status: 200, - fixture: 'cat-facts' - }) - cy.get('button[class=new-fact-btn]').click() - cy.get('p').contains('Cats can see like wicked good') - cy.reload() - cy.get('p').contains('Cats can see like wicked good') + cy.get('.fav').should('be.visible') + cy.get('.left').should('be.visible') + cy.get('.right').should('be.visible') }) }) \ No newline at end of file diff --git a/cypress/e2e/favorites_spec.cy.js b/cypress/e2e/favorites_spec.cy.js index f0ceef5..c3190fb 100644 --- a/cypress/e2e/favorites_spec.cy.js +++ b/cypress/e2e/favorites_spec.cy.js @@ -1,28 +1,43 @@ describe('favorites page', () => { - it('should be able to favorite a fact', () => { - cy.intercept('https://catfact.ninja/fact', { + beforeEach(() => { + cy.intercept('https://api.thecatapi.com/v1/images/search?size=med&mime_types=jpg&format=json&has_breeds=true&order=RANDOM&page=0&limit=10', { status: 200, fixture: 'cat-facts' }) cy.visit('http://localhost:3000/fact') - cy.get('button[class=new-fact-btn]').click() - cy.get('p').contains('Cats can see like wicked good') - cy.get('button[class=fav-card-btn]').click() - cy.get('div[class=nav-links]').children().next().next().click() - cy.get('p[class=fav-card-fact]').contains('Cats can see like wicked good') + cy.get('.name').contains('Siamese') + cy.get('.fav').click() + cy.get('.nav-links').children().next().next().click() }) - it('should have a card and a way to delete the card', () => { - cy.intercept('https://catfact.ninja/fact', { - status: 200, - fixture: 'cat-facts' - }) - cy.visit('http://localhost:3000/fact') - cy.get('button[class=new-fact-btn]').click() - cy.get('button[class=fav-card-btn]').click() - cy.get('div[class=nav-links]').children().next().next().click() - cy.get('div[class=fav-card]').should('have.length', 1) - cy.get('button[class=delete-btn]:first').click() - cy.get('section[class=fav-card-display]').contains('No saved ideas yet') + it('should contain a favorited fact', () => { + cy.get('.fav-card-name').contains('Siamese') + }) + + it('should have have a delete button', () => { + cy.get('.card-btn-container').invoke('show') + cy.get('.delete-btn').invoke('show').click() + cy.get('.no-ideas').contains('No saved ideas yet') + }) + + it('should have an expand button', () => { + cy.get('.card-btn-container').invoke('show') + cy.get('.expand-btn').invoke('show').click() + cy.get('.exp-name').contains('Siamese') + cy.get('.exp-description').contains('While Siamese cats are extremely fond of their people, they will follow you around and supervise your every move, being talkative and opinionated. They are a demanding and social cat, that do not like being left alone for long periods.') + cy.get('.exp-stats-title').contains('Stats') + cy.get('.exp-dogFriendly').contains('Yes') + cy.get('.exp-energyLevel').contains('5') + cy.get('.exp-origin').contains('Thailand') + cy.get('.exp-temperament').contains('Active, Agile, Clever, Sociable, Loving, Energetic') + cy.get('.exp-links-title').contains('External Links') + cy.get('.vca-link').contains('VCA') + cy.get('.wiki-link').contains('Wikipedia') + }) + + it('should have a button to return to favorites view', () => { + cy.get('.card-btn-container').invoke('show') + cy.get('.expand-btn').invoke('show').click() + cy.get('.remove-card').click() }) }) \ No newline at end of file diff --git a/cypress/fixtures/cat-facts.json b/cypress/fixtures/cat-facts.json index c009845..0b5a729 100644 --- a/cypress/fixtures/cat-facts.json +++ b/cypress/fixtures/cat-facts.json @@ -1,3 +1,49 @@ -{ - "fact": "Cats can see like wicked good" +[{ + "breeds": [{ + "adaptability": "5", + "affection_level": "5", + "alt_names": "Siam, Thai Cat", + "cfa_url": "http://cfa.org/Breeds/BreedsSthruT/Siamese.aspx", + "child_friendly": "4", + "country_code": "TH", + "country_codes": "TH", + "description": "While Siamese cats are extremely fond of their people, they will follow you around and supervise your every move, being talkative and opinionated. They are a demanding and social cat, that do not like being left alone for long periods.", + "dog_friendly": "5", + "energy_level": "5", + "experimental": "0", + "grooming": "1", + "hairless": "0", + "health_issues": "1", + "hypoallergenic": "1", + "id": "siam", + "indoor": "0", + "intelligence": "5", + "lap": "1", + "life_span": "12 - 15", + "name": "Siamese", + "natural": "0", + "origin": "Thailand", + "rare": "0", + "reference_image_id": "ai6Jps4sx", + "rex": "0", + "shedding_level": "2", + "short_legs": "0", + "social_needs": "5", + "stranger_friendly": "5", + "suppressed_tail": "0", + "temperament": "Active, Agile, Clever, Sociable, Loving, Energetic", + "vcahospitals_url": "https://vcahospitals.com/know-your-pet/cat-breeds/siamese", + "vetstreet_url": "http://www.vetstreet.com/cats/siamese", + "vocalisation": 5, + "weight": { + "imperial": "8 - 15", + "metric": "4 - 7" + }, + "wikipedia_url": "https://en.wikipedia.org/wiki/Siamese_(cat)" + }], + "height": "809", + "id": "uTG1YFzJV", + "url": "https://cdn2.thecatapi.com/images/uTG1YFzJV.jpg", + "width": "1080" } +] \ No newline at end of file diff --git a/src/components/About/About.css b/src/components/About/About.css index e161a92..bc09b33 100644 --- a/src/components/About/About.css +++ b/src/components/About/About.css @@ -1,6 +1,6 @@ .about-box { display: flex; - background-color: pink; + background-color: #4b5d67; } .how-list { diff --git a/src/components/App/App.css b/src/components/App/App.css index ad4b467..02cb5a4 100644 --- a/src/components/App/App.css +++ b/src/components/App/App.css @@ -1,6 +1,8 @@ html { height: 100%; + background-color: black; } + main { height: 100%; -} +} \ No newline at end of file diff --git a/src/components/App/App.js b/src/components/App/App.js index 12849ac..231b95f 100644 --- a/src/components/App/App.js +++ b/src/components/App/App.js @@ -1,29 +1,64 @@ import './App.css'; -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import Home from '../Home/Home'; -import Fact, { factLoader } from '../Fact/Fact'; +import Fact from '../Fact/Fact'; import { RouterProvider, createBrowserRouter } from 'react-router-dom'; import Favorites from '../Favorites/Favorites'; import Error from '../Error/Error'; import RootLayout from '../../utilities/RootLayout'; +import dataCleaner from '../../utilities/dataCleaner'; +import { getCatPhotos } from '../../utilities/api-call'; const App = () => { - const [favorites, setFavorites] = useState(JSON.parse(sessionStorage.getItem('allFavorites')) || []); + const [favoritedFacts, setFavoritedFacts] = useState([]); + const [cats, setCats] = useState([]); + const [count, setCount] = useState(0); + const [err, setErr] = useState() + + useEffect(() => { + if (cats.length === 0) { + getCatPhotos() + .then(res => { + setCats(dataCleaner(res)) + }) + .catch(err => { + console.log(err) + setErr(err) + + }) + } + },[cats]) const favoriteFact = (currentFact) => { - const noDuplicate = favorites.every(fact => fact.fact !== currentFact.fact) - + const noDuplicate = favoritedFacts.every(fact => fact.id !== currentFact.id) if (noDuplicate) { - setFavorites([...favorites, currentFact]) - sessionStorage.setItem('allFavorites', JSON.stringify([...favorites, currentFact])) + currentFact.favorited = true; + setFavoritedFacts([...favoritedFacts, currentFact]) + const updatedCats = cats.map((cat) => { + if (cat.id === currentFact.id) { + return currentFact + } + return cat + }); + setCats([...updatedCats]) } } const removeFav = (id) => { - sessionStorage.removeItem('allFavorites') - const newFavs = favorites.filter(fact => fact.id !== id) - setFavorites(newFavs) - sessionStorage.setItem('allFavorites', JSON.stringify(newFavs)) + const updatedCats = cats.map((cat) => { + if (cat.id === id) { + cat.favorited = false; + return cat + } + return cat + }); + setCats([...updatedCats]) + const newFavs = favoritedFacts.filter(fact => fact.id !== id) + setFavoritedFacts(newFavs) + } + + const addData = (data) => { + setCats([...cats, ...data]) } const routes = createBrowserRouter([ @@ -38,17 +73,25 @@ const App = () => { }, { path: "/fact", - element: , - loader: factLoader + element: , }, { path: "/favorites", - element: + element: } ] } ]) - + return ( ) diff --git a/src/components/Card/Card.css b/src/components/Card/Card.css index 93e7c3d..eaf67f4 100644 --- a/src/components/Card/Card.css +++ b/src/components/Card/Card.css @@ -1,29 +1,82 @@ .fav-card { display: flex; width: 18%; - border: solid 5px black; border-radius: 10px; - background-color: #ff9c00; + background-color: #ffffff; color: black; font-weight: 700; text-align: center; - padding: 5px; margin: 10px; - height: 10em; + height: 18em; flex-direction: column; justify-content: space-between; + position: relative; } -.fav-card-fact { - overflow: scroll; +.card-thumb { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 7px; } -.delete-btn { +.fav-card-name { + position: absolute; + bottom: 0; + background: #ffffff8a; + width: 100%; + margin: 0; + padding: 16px 0px; + border-bottom-left-radius: 7px; + border-bottom-right-radius: 7px; +} + +.card-btn-container { + opacity: 0; + transition: opacity 0.56s ease-in-out; + position: absolute; + width: 26%; + background-color: #ffffff8a; + right: 0; + top: 16px; + border-top-left-radius: 7px; + border-bottom-left-radius: 7px; + /* height: 50%; */ +} + +.fav-card:hover .card-btn-container, +.fav-card:hover .fav-card-name { + display: block; + opacity: 1; +} + +.delete-btn, +.expand-btn { cursor: pointer; + width: 35px; + padding-top: 5px; } @media (max-width: 900px) { .fav-card { width: 30%; } +} + +@media (max-width: 730px) { + .fav-card { + width: 50%; + } +} + +@media (max-width: 500px) { + .fav-card { + width: 70%; + } +} + +@media (hover: none) { + .card-btn-container { + opacity: 1; + } } \ No newline at end of file diff --git a/src/components/Card/Card.js b/src/components/Card/Card.js index 92946a4..c384d29 100644 --- a/src/components/Card/Card.js +++ b/src/components/Card/Card.js @@ -1,14 +1,18 @@ import React from 'react'; import './Card.css'; import PropTypes from 'prop-types'; +import trashIcon from '../../utilities/trash-icon.png'; +import expandIcon from '../../utilities/expand-icon.png'; -const Card = ({ description, removeFav, id }) => { +const Card = ({ name, removeFav, id, image, cardSelect, stats }) => { return (
-

{description}

- + {`${name} +
+ trash icon removeFav(id)} /> + expand icon cardSelect(stats)} /> +
+

{name}

); }; @@ -16,7 +20,7 @@ const Card = ({ description, removeFav, id }) => { export default Card; Card.propTypes = { - description: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, removeFav: PropTypes.func.isRequired, - id: PropTypes.number.isRequired + id: PropTypes.string.isRequired } \ No newline at end of file diff --git a/src/components/Error/Error.css b/src/components/Error/Error.css index 462b7bd..b594bab 100644 --- a/src/components/Error/Error.css +++ b/src/components/Error/Error.css @@ -8,6 +8,11 @@ width: 65em; } +.error-message { + color: white; + text-align: center; +} + @media (max-width: 847px) { .error-img { width: 100%; diff --git a/src/components/ExpandedCard/ExpandedCard.css b/src/components/ExpandedCard/ExpandedCard.css new file mode 100644 index 0000000..7c7da88 --- /dev/null +++ b/src/components/ExpandedCard/ExpandedCard.css @@ -0,0 +1,183 @@ +.expanded-background { + position: absolute; + background-color: rgba(255, 255, 255, 0.457); + width: 100%; + height: 100%; +} + +.expanded-card { + position: absolute; + width: 70%; + height: 64%; + left: 15%; + bottom: 28%; + background-color: white; + border-radius: 10px; + display: flex; + align-items: center; + z-index: 1; +} + +.stats-area { + padding: 10px; +} + +.remove-card { + cursor: pointer; + width: 35px; + position: absolute; + top: 5px; + right: 5px; + background-color: #cfcfcf8c; + border-radius: 9px; +} + +.expanded-image-container { + min-width: 50%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; +} + +.expanded-image { + height: 90%; + max-width: 90%; + object-fit: cover; + border-radius: 10px; +} + +.exp-links-container { + display: flex; + justify-content: space-between; +} + +.exp-stats-title, +.exp-links-title { + font-weight: 600; + font-size: large; +} + +.exp-name { + font-weight: 900; + font-size: xx-large; + margin-left: 10px; +} + +.stats-dropdown, +.stats-dropup { + display: none; +} + +.wiki-link { + margin-right: 20px; +} + +@media (max-width: 1110px) { + .stats-column-con { + display: flex; + flex-direction: column; + } + + .stats-column-con-active { + display: flex; + flex-direction: column; + position: absolute; + top: 35%; + } + + .stats-dropdown { + display: block; + width: 25px; + height: 25px; + margin: 10px 10px 30px; + transform: rotate(90deg); + } + + .stats-dropup { + display: block; + transform: rotate(-90deg); + width: 25px; + height: 25px; + margin: 10px 10px 30px; + } + + .hide-stats { + display: none; + } + + .exp-name { + margin: 10px 0 30px 10px; + } + + .expanded-image-container, + .expanded-image { + width: 100%; + } + + .expanded-image { + min-width: 100%; + min-height: 100%; + } + + .stats-area { + position: absolute; + height: 100%; + width: 100%; + display: flex; + flex-direction: column-reverse; + padding: unset; + } + + .stats-column-1 { + width: 100%; + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + display: flex; + align-items: center; + justify-content: space-between; + background-image: linear-gradient(rgb(255, 255, 255, 0), rgb(255, 255, 255, 1)); + } + + .active-drop { + border-bottom-left-radius: unset; + border-bottom-right-radius: unset; + } + + .stats-column-2 { + border-bottom-left-radius: 10px; + border-bottom-right-radius: 10px; + width: 100%; + background-color: white; + } + + .stats-column-1 { + background-color: rgba(255, 255, 255, 0.5); + } + + .exp-description { + margin-right: 20px; + } + + .exp-links-container { + margin-bottom: 20px; + } + + .drop { + margin-left: 10px; + } + + .exp-links-container { + justify-content: space-around; + } + + .wiki-link { + margin-right: unset; + } +} +@media (max-width: 900px) { + .expanded-card { + width: 90%; + left: 5%; + } +} diff --git a/src/components/ExpandedCard/ExpandedCard.js b/src/components/ExpandedCard/ExpandedCard.js new file mode 100644 index 0000000..8415374 --- /dev/null +++ b/src/components/ExpandedCard/ExpandedCard.js @@ -0,0 +1,51 @@ +import './ExpandedCard.css'; +import { useState } from 'react'; +import cancelIcon from '../../utilities/x.svg'; +import expandArrow from '../../utilities/expand-arrow.png'; + +const ExpandedCard = ({ stats, removeCard }) => { + + const [dropdown, setDropdown] = useState(false) + + const triggerDropdown = () => { + setDropdown(!dropdown) + } + + return ( +
{ + if (event.target.className === "expanded-background") { + removeCard() + } + }}> +
+
+ {`${stats.name} +
+
+
+
+

{stats.name}

+ expand-stats +
+
+

{`Description: ${stats.description}`}

+

Stats

+

{`Dog Friendly: ${stats.dogFriendly}`}

+

{`Energy Level: ${stats.energyLevel}`}

+

{`Origin: ${stats.origin}`}

+

{`Temperament: ${stats.temperament}`}

+

External Links

+
+ VCA + Wikipedia +
+
+
+ +
+
+
+ ) +} + +export default ExpandedCard; \ No newline at end of file diff --git a/src/components/Fact/Fact.css b/src/components/Fact/Fact.css index 06d06f7..f94191b 100644 --- a/src/components/Fact/Fact.css +++ b/src/components/Fact/Fact.css @@ -1,92 +1,91 @@ .fact-page { display: flex; - flex-direction: column; align-items: center; background-color: black; + justify-content: center; + height: 79vh; } .fact-display { - display: flex; - width: 80%; - color: white; - align-items: center; + background-color: white; + border-radius: 15%; } -.fact, -.fact-img { - width: 50%; - height: 34em; - margin: 0; +.inactive-arrow-left { + opacity: 0.2; } -.fact { - padding: 0 1em 1em; - font-size: large; - font-weight: 600; - display: flex; - align-items: center; - text-align: center; - overflow: scroll; +.arrow-right { + -webkit-transform: scaleX(-1); + transform: scaleX(-1); + right: 50%; + margin-right: 20px; } -.fact-img { - object-fit: cover; +.inactive-arrow-left, +.arrow-left, +.arrow-right { + width: 66px; + height: 66px; + position: absolute; + bottom: 3%; } -.button-container { - display: flex; - background-color: pink; - justify-content: space-evenly; - width: 100%; - height: 10em; +.arrow-left, +.arrow-right { + cursor: pointer; } -.fav-card-btn, -.new-fact-btn { - cursor: pointer; - border-radius: 10px; - width: 15%; - height: 40%; - background-color: #64c79f; - border: 0; - color: black; - margin-top: 4em; +.inactive-container { + width: 86px; + height: 86px; + border-radius: 50px; + margin: 20px; } -.new-fact-btn:hover, -.fav-card-btn:hover { - background-color: #3ba178; - border-bottom: solid black 4px; +.arrow-container:hover { + cursor: pointer; } -.new-fact-btn:active, -.fav-card-btn:active { - background-color: #ffffff; - border-bottom: 0px; +.inactive-container:hover { + cursor: default; } -.new-fact-btn:after, -.fav-card-btn:after { - transition: 0.5s; +.signals { + color: white; } +.custom-err { + color: white; +} @media (max-width: 1250px) { - .fact, - .fact-img { - height: 27em; - } - - .fact-img { - width: 100%; - } - .fact-display{ flex-direction: column; } .fav-card-btn, .new-fact-btn { - width: 35%; + width: 25%; + } + + .inactive-arrow-left, + .arrow-left, + .arrow-right { + bottom: 0%; + z-index: 1; + background-color: rgba(255, 255, 255, 0.5); + border-bottom-left-radius: 15px; + border-top-right-radius: 15px; + } + + .inactive-arrow-left, + .arrow-left { + left: 0%; + } + + .arrow-right { + right: 0%; + margin: unset; } } \ No newline at end of file diff --git a/src/components/Fact/Fact.js b/src/components/Fact/Fact.js index 6353a40..af6701f 100644 --- a/src/components/Fact/Fact.js +++ b/src/components/Fact/Fact.js @@ -1,57 +1,69 @@ -import React, { useState } from 'react'; +import React, { useEffect } from 'react'; import './Fact.css'; -import getCatFacts from '../../utilities/api-call'; +import { getCatPhotos } from '../../utilities/api-call'; import PropTypes from 'prop-types'; -import { useLoaderData } from 'react-router-dom'; +import dataCleaner from '../../utilities/dataCleaner'; +import FactCard from '../FactCard/FactCard'; +import arrow from '../../utilities/arrow-icon.png'; -const Fact = ({ favFact }) => { - const randFact = useLoaderData() - const savedFact = JSON.parse(sessionStorage.getItem('currentFact')) - - const [currentFact, setCurrentFact] = useState(savedFact || randFact); - - const setFact = (fact) => { - setCurrentFact({fact: fact}); - sessionStorage.setItem('currentFact', JSON.stringify({fact: fact})) - } +const Fact = ({ favFact, removeFav, setCats, cats, catCount, saveCatCount }) => { + const fetchFact = () => { - getCatFacts('https://catfact.ninja/fact') - .then(data => { - setFact(data.fact); + getCatPhotos() + .then(res => { + const cleanedDetails = dataCleaner(res); + setCats([...cats, ...cleanedDetails]); + }) + .catch(err => { + console.log(err) }) } + const leftArrow = 0 ? "arrow-left" : "inactive-arrow-left"} + src={arrow} + alt='arrow left icon' + onClick={() => { + if (catCount > 0) { + saveCatCount(catCount -= 1) + } + }} /> + + const rightArrow = arrow right icon { + if (catCount === catSpecs.length - 2) { + fetchFact(); + } + saveCatCount(catCount += 1) + }} /> + + const catSpecs = cats.map(cat => ); + + useEffect(()=> { + },[favFact, removeFav]) + return (
-
-

{currentFact.fact}

- Cat with butterfly on nose -
-
- - - -
+ + + {catSpecs[catCount]} + +
) }; export default Fact; -export const factLoader = async () => { - const res = await fetch('https://catfact.ninja/fact') - - return res.json() -} - Fact.propTypes = { - randFact: PropTypes.string, favFact: PropTypes.func.isRequired } \ No newline at end of file diff --git a/src/components/FactCard/FactCard.css b/src/components/FactCard/FactCard.css new file mode 100644 index 0000000..870db7f --- /dev/null +++ b/src/components/FactCard/FactCard.css @@ -0,0 +1,137 @@ +.fact-display { + display: flex; + width: 80%; + color: black; + align-items: center; + background-color: rgb(255, 255, 255); + margin: 30px 0; + border-radius: 19px; + position: relative; + height: 75vh; +} + +.cat-specs { + display: flex; + flex-direction: column; + align-items: center; + height: 100%; +} + +.fact-img { + min-width: 50%; + margin: 20px; + border-radius: 15px; + object-fit: cover; + max-height: 90%; + min-height: 90%; + box-shadow: 0px 9px 9px black; +} + +.name { + font-size: xxx-large; + font-weight: 900; + margin-left: 20px; +} + +.name-fav-con { + display: flex; + justify-content: space-between; + align-items: center; +} + +.fact, +.dog-friendly, +.energy-level, +.affection-level, +.origin, +.temperament { + font-size: large; + font-weight: 600; + overflow: scroll; + margin-left: 20px; +} + +.vote-icon { + width: 49px; + z-index: -1; + cursor: pointer; +} + +.vote-icon-container { + height: 49px; + background-color: lightgrey; + border-radius: 50px; + box-shadow: 0px 5px 3px black; +} + +.fav { + background-color: #3d85c6; + border-radius: 50px; +} + +@media (max-width: 1252px) { + .fact, + .temperament, + .affection-level, + .energy-level, + .dog-friendly, + .origin { + display: none; + } + + .fact-display { + position: relative; + } + + .name-fav-con { + position: absolute; + border-top-left-radius: 15px; + border-top-right-radius: 15px; + background-color: rgba(255, 255, 255, 0.5); + width: 100%; + left: 0; + } + + .vote-icon-container { + margin-right: 5px; + } + + .name { + margin: 5px; + top: 0; + left: 0; + z-index: 1; + } + + .vote-icon { + z-index: 0; + } + + .fact-img { + width: 100%; + min-height: 100%; + max-height: 100%; + margin: unset; + } + + .arrow-box-right, + .arrow-box-left { + position: absolute; + background-color: rgba(255, 255, 255, 0.5); + width: 10%; + height: 12%; + bottom: 0%; + } + + .arrow-box-right { + right: 0%; + border-bottom-right-radius: 15px; + border-top-left-radius: 15px; + } + + .arrow-box-left { + left: 0%; + border-bottom-left-radius: 15px; + border-top-right-radius: 15px; + } +} \ No newline at end of file diff --git a/src/components/FactCard/FactCard.js b/src/components/FactCard/FactCard.js new file mode 100644 index 0000000..5108cb1 --- /dev/null +++ b/src/components/FactCard/FactCard.js @@ -0,0 +1,38 @@ +import './FactCard.css'; +import heart from '../../utilities/heart.png'; + +const FactCard = ({ details, favFact, removeFav, leftArrow, rightArrow }) => { + + return ( +
+
+
+
+

{details.name}

+
+
+ favorite button { + details.favorited ? removeFav(details.id) : favFact(details); + }}/> +
+
+
+

{`Dog Friendly: ${details.dogFriendly}`}

+

{`Energy Level: ${details.energyLevel}`}

+

{`Affection Level: ${details.affectionLevel}`}

+

{`Origin: ${details.origin}`}

+

{`Temperaments: ${details.temperament}`}

+

{`Description: ${details.description}`}

+
+ + +
+ {`A + {leftArrow} + {rightArrow} +
+ ) +} + +export default FactCard; \ No newline at end of file diff --git a/src/components/Favorites/Favorites.css b/src/components/Favorites/Favorites.css index 5640870..d9adb1a 100644 --- a/src/components/Favorites/Favorites.css +++ b/src/components/Favorites/Favorites.css @@ -1,8 +1,12 @@ .fav-card-display { - height: 50em; - background-color: salmon; + background-color: rgb(0, 0, 0); display: flex; flex-wrap: wrap; justify-content: center; align-content: flex-start; + /* position: relative; */ +} + +.no-ideas { + color: white; } \ No newline at end of file diff --git a/src/components/Favorites/Favorites.js b/src/components/Favorites/Favorites.js index 8a61926..195b011 100644 --- a/src/components/Favorites/Favorites.js +++ b/src/components/Favorites/Favorites.js @@ -1,23 +1,44 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import './Favorites.css'; import Card from '../Card/Card'; import PropTypes from 'prop-types'; +import ExpandedCard from '../ExpandedCard/ExpandedCard'; const Favorites = ({ favs, removeFav }) => { + + const [selected, setSelected] = useState(undefined); + + const selectCard = (card) => { + setSelected(card) + } + + const deselectCard = () => { + setSelected(undefined) + } + const savedCards = favs.map(fact => { return }) - + + useEffect(() => { + + },[selected]) + return (
{!savedCards.length ?

No saved ideas yet

: savedCards} + {selected ? : + <>}
); }; diff --git a/src/components/Header/Header.css b/src/components/Header/Header.css index 187ac4f..6103681 100644 --- a/src/components/Header/Header.css +++ b/src/components/Header/Header.css @@ -1,7 +1,7 @@ .header { display: flex; justify-content: space-evenly; - background-color: rgb(7, 241, 218); + background-color: #4b5d67; } .logo { @@ -10,8 +10,35 @@ margin: 10px; } +.logo:hover { + animation-name: spin; + animation-duration: 5000ms; + animation-iteration-count: infinite; + animation-timing-function: linear; +} + +@keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + .app-title { font-size: xxx-large; + transition: text-shadow 0.56s ease-in-out; +} + +.app-title:hover { + text-shadow: #ffffbf 0 0 20px, + #6effe4 0 0 40px; +} + +.app-title:active { + text-shadow: unset; + transition: unset; } .link, @@ -23,15 +50,20 @@ .link { margin: 5px; font-size: large; - font-weight: 500; + font-weight: 600; } .link:hover { - color: rgb(244, 23, 23); - background-color: black; + color: rgb(255, 255, 255); + border-bottom: solid 2px rgb(255, 255, 255); border-radius: 2px; } +.link:active { + border-bottom: unset; + color: unset; +} + .hidden { display: none; } @@ -52,6 +84,10 @@ text-align: center; } + .logo { + display: none; + } + .header { flex-direction: column; align-items: center; diff --git a/src/components/Home/Home.css b/src/components/Home/Home.css index c265a2e..09311e4 100644 --- a/src/components/Home/Home.css +++ b/src/components/Home/Home.css @@ -1,28 +1,58 @@ .hero { height: 39em; + position: relative; } .hero-img { - position: relative; width: 100%; overflow: hidden; height: 39em; object-fit: cover; } +.home-btn-con { + position: absolute; + width: 50%; + display: flex; + justify-content: space-around; + bottom: 0; + left: 25%; + background-color: rgba(255, 255, 255, 0.5); + height: 12%; + border-top-left-radius: 15px; + border-top-right-radius: 15px; + align-items: center; +} + .fact-btn, .fav-btn { - position: absolute; - bottom: 22%; + bottom: 2%; cursor: pointer; - border-radius: 10px; + border-radius: 27px; width: 15%; height: 5%; - background-color: brown; + background-color: #4b5d67; border: 0; + color: black; + font-weight: 800; + width: 150%; + height: 35px; +} + +.fact-btn:hover, +.fav-btn:hover { + background-color: #262a2d; + border-bottom: solid black 4px; color: white; } +.fact-btn:active, +.fav-btn:active { + background-color: #ffffff; + border-bottom: 0px; + color: black; +} + .fact-btn { right: 30%; @@ -42,8 +72,6 @@ @media (max-width: 750px) { .fact-btn, .fav-btn { - position: absolute; - bottom: 5px; height: 25px; } diff --git a/src/components/Home/Home.js b/src/components/Home/Home.js index d589978..75a1c2c 100644 --- a/src/components/Home/Home.js +++ b/src/components/Home/Home.js @@ -2,16 +2,23 @@ import React from 'react'; import './Home.css'; import About from '../About/About' import { Link } from 'react-router-dom'; +import kitty from '../../utilities/home-page-kitty.jpg'; +import bigKitty from '../../utilities/full-size-kitty.jpg'; const Home = () => { + +const screenWidth = window.screen.width; + +const homeCat = screenWidth > 1000 ? bigKitty : kitty; + return (
- Cat lying on its back - - + Cat lying on its back +
+ + +
diff --git a/src/utilities/api-call.js b/src/utilities/api-call.js index 1718e73..7f44bef 100644 --- a/src/utilities/api-call.js +++ b/src/utilities/api-call.js @@ -1,12 +1,21 @@ -const getCatFacts = (url) => { - return fetch(url) +const getCatPhotos = () => { + return fetch('https://api.thecatapi.com/v1/images/search?size=med&mime_types=jpg&format=json&has_breeds=true&order=RANDOM&page=0&limit=10', { + method: 'GET', + headers: { + "Content-Type": "application/json", + "x-api-key" : "live_FuFFXKaSKCVUcKTZnk9uNsM0iH56QnCHDr36p2iLRV7HIYpsbrjBdkcThRCCUjQT" + } + }) .then(res => { if (!res.ok) { throw new Error(res.status); } else { return res.json(); } - }); -}; + }) + .catch(err => { + console.log(err) + }) +} -export default getCatFacts; \ No newline at end of file +export { getCatPhotos }; \ No newline at end of file diff --git a/src/utilities/arrow-icon.png b/src/utilities/arrow-icon.png new file mode 100644 index 0000000..d042aa1 Binary files /dev/null and b/src/utilities/arrow-icon.png differ diff --git a/src/utilities/dataCleaner.js b/src/utilities/dataCleaner.js new file mode 100644 index 0000000..18260fa --- /dev/null +++ b/src/utilities/dataCleaner.js @@ -0,0 +1,22 @@ +const dataCleaner = (catApiData) => { + + return catApiData.map(cat =>{ + + return { + affectionLevel: cat.breeds[0].affection_level, + description: cat.breeds[0].description, + dogFriendly: cat.breeds[0].dog_friendly >= 5 ? "Yes" : "No", + energyLevel: cat.breeds[0].energy_level, + id: cat.id, + image: cat.url, + name: cat.breeds[0].name, + origin: cat.breeds[0].origin, + temperament: cat.breeds[0].temperament, + vcaUrl: cat.breeds[0].vcahospitals_url, + wikiUrl: cat.breeds[0].wikipedia_url, + favorited: false, + } + }) +} + +export default dataCleaner; \ No newline at end of file diff --git a/src/utilities/expand-arrow.png b/src/utilities/expand-arrow.png new file mode 100644 index 0000000..1189972 Binary files /dev/null and b/src/utilities/expand-arrow.png differ diff --git a/src/utilities/expand-icon.png b/src/utilities/expand-icon.png new file mode 100644 index 0000000..34b4bac Binary files /dev/null and b/src/utilities/expand-icon.png differ diff --git a/src/utilities/full-size-kitty.jpg b/src/utilities/full-size-kitty.jpg new file mode 100644 index 0000000..93f4533 Binary files /dev/null and b/src/utilities/full-size-kitty.jpg differ diff --git a/src/utilities/heart.png b/src/utilities/heart.png new file mode 100644 index 0000000..3c5edd0 Binary files /dev/null and b/src/utilities/heart.png differ diff --git a/src/utilities/home-page-kitty.jpg b/src/utilities/home-page-kitty.jpg new file mode 100644 index 0000000..ccbc4c4 Binary files /dev/null and b/src/utilities/home-page-kitty.jpg differ diff --git a/src/utilities/test-data.js b/src/utilities/test-data.js new file mode 100644 index 0000000..7bf83c3 --- /dev/null +++ b/src/utilities/test-data.js @@ -0,0 +1,99 @@ +const testData = [{ + breeds: [{ + adaptability: 5, + affection_level: 5, + alt_names: "Siam, Thai Cat", + cfa_url: "http://cfa.org/Breeds/BreedsSthruT/Siamese.aspx", + child_friendly: 4, + country_code: "TH", + country_codes: "TH", + description: "While Siamese cats are extremely fond of their people, they will follow you around and supervise your every move, being talkative and opinionated. They are a demanding and social cat, that do not like being left alone for long periods.", + dog_friendly: 5, + energy_level: 5, + experimental: 0, + grooming: 1, + hairless: 0, + health_issues: 1, + hypoallergenic: 1, + id: "siam", + indoor: 0, + intelligence: 5, + lap: 1, + life_span: "12 - 15", + name: "Siamese", + natural: 0, + origin: "Thailand", + rare: 0, + reference_image_id: "ai6Jps4sx", + rex: 0, + shedding_level: 2, + short_legs: 0, + social_needs: 5, + stranger_friendly: 5, + suppressed_tail: 0, + temperament: "Active, Agile, Clever, Sociable, Loving, Energetic", + vcahospitals_url: "https://vcahospitals.com/know-your-pet/cat-breeds/siamese", + vetstreet_url: "http://www.vetstreet.com/cats/siamese", + vocalisation: 5, + weight: { + imperial: "8 - 15", + metric: "4 - 7" + }, + wikipedia_url: "https://en.wikipedia.org/wiki/Siamese_(cat)" + }], + height: 809, + id: "uTG1YFzJV", + url: "https://cdn2.thecatapi.com/images/uTG1YFzJV.jpg", + width: 1080 +}, +{ + breeds: [{ + adaptability: 5, + affection_level: 5, + alt_names: "Siam, Thai Cat", + cfa_url: "http://cfa.org/Breeds/BreedsSthruT/Siamese.aspx", + child_friendly: 4, + country_code: "TH", + country_codes: "TH", + description: "While Siamese cats are extremely fond of their people, they will follow you around and supervise your every move, being talkative and opinionated. They are a demanding and social cat, that do not like being left alone for long periods.", + dog_friendly: 5, + energy_level: 5, + experimental: 0, + grooming: 1, + hairless: 0, + health_issues: 1, + hypoallergenic: 1, + id: "siam", + indoor: 0, + intelligence: 5, + lap: 1, + life_span: "12 - 15", + name: "I swear this isn't a siamese cat", + natural: 0, + origin: "Def not Thailand", + rare: 0, + reference_image_id: "ai6Jps4sx", + rex: 0, + shedding_level: 2, + short_legs: 0, + social_needs: 5, + stranger_friendly: 5, + suppressed_tail: 0, + temperament: "Active, Agile, Clever, Sociable, Loving, Energetic", + vcahospitals_url: "https://vcahospitals.com/know-your-pet/cat-breeds/siamese", + vetstreet_url: "http://www.vetstreet.com/cats/siamese", + vocalisation: 5, + weight: { + imperial: "8 - 15", + metric: "4 - 7" + }, + wikipedia_url: "https://en.wikipedia.org/wiki/Siamese_(cat)" + }], + height: 809, + id: "uTG1YFzJV", + url: "https://cdn2.thecatapi.com/images/uTG1YFzJV.jpg", + width: 1080 +} +] + +export default testData; \ No newline at end of file diff --git a/src/utilities/trash-icon.png b/src/utilities/trash-icon.png new file mode 100644 index 0000000..895e47f Binary files /dev/null and b/src/utilities/trash-icon.png differ