diff --git a/src/components/App.css b/src/components/App.css index 464aa67..af0b5ce 100644 --- a/src/components/App.css +++ b/src/components/App.css @@ -37,6 +37,10 @@ flex-wrap: wrap; } +.toggle-buttons { + padding: 1rem; +} + @keyframes App-logo-spin { from { transform: rotate(0deg); diff --git a/src/components/App.tsx b/src/components/App.tsx index 120fdfb..e67ac33 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -1,49 +1,97 @@ import React, { useState } from 'react'; -import logo from '../logo.svg'; +import { QueryClient, QueryClientProvider } from 'react-query'; import './App.css'; import PersonCard from './PersonCard'; +import ToggleButton from 'react-bootstrap/ToggleButton'; +import ToggleButtonGroup from 'react-bootstrap/ToggleButtonGroup'; + +const queryClient = new QueryClient(); function App() { - const [length, setLength] = useState(12); // Initial length is set to 12 + const [length, setLength] = useState(12); + const [favoriteCards, setFavoriteCards] = useState([]); + const [showFavorites, setShowFavorites] = useState(false); - const handleInputChange = (event: any) => { + const handleInputChange = (event: React.ChangeEvent) => { const value = parseInt(event.target.value); - let result = Math.min(value, 50); // while , greater numbers can be entered into the input -> ensure maximum of 50 to limit API calls - console.log(result); - setLength(result); + if (!isNaN(value)) { + // cap maximum number of cards to limit API calls and ignore invalid data + let result = Math.min(Math.max(value, 1), 50); + setLength(result); + } + }; + + // Call AWS lambda to update DynamoDB here!! + const handleFavoriteToggle = (cardIndex: number) => { + setFavoriteCards(prevFavorites => + prevFavorites.includes(cardIndex) + ? prevFavorites.filter(id => id !== cardIndex) + : [...prevFavorites, cardIndex] + ); + }; + + // Switch between 'All-Items' and 'Favorites' + const handleToggleChange = (value: number) => { + setShowFavorites(value === 2); }; + const displayedCards = showFavorites + ? favoriteCards + : Array.from({ length }, (_, index) => index); + return ( -
-
-

SWAPI React + Typescript Test!

- +
+
+

SWAPI React + Typescript Test!

+
+ Star Wars API + +
+
+ + +
+ + - Star Wars API - -
-
- - -
- {/* Create an array of specified length and render a PersonCard component for each element*/} -
- {Array.from({ length }).map((_, index) => ( - - ))} + + All-Items + + + Favorites + + + +
+ {displayedCards.map(index => ( + + ))} +
-
+ ); } diff --git a/src/components/PersonCard.tsx b/src/components/PersonCard.tsx index 5038ddb..affd13b 100644 --- a/src/components/PersonCard.tsx +++ b/src/components/PersonCard.tsx @@ -1,41 +1,38 @@ +import { useQuery } from 'react-query'; import Card from 'react-bootstrap/Card'; - -import { QueryClient, QueryClientProvider, useQuery } from 'react-query'; -import { Link } from 'react-router-dom' - -const queryClient = new QueryClient() +import ToggleButton from 'react-bootstrap/ToggleButton'; interface PersonCardProps { index: number; -}; - -interface CharacterDataProps { - person: number; + isFavorite: boolean; + onFavoriteToggle: (cardIndex: number) => void; } -function PersonCard({ index }: PersonCardProps) { - return ( - - - - ); +interface CharacterData { + name: string; + height: string; + mass: string; + eye_color: string; + hair_color: string; + skin_color: string; } -function CharacterData({ person }: CharacterDataProps): JSX.Element { +function PersonCard({ index, isFavorite, onFavoriteToggle }: PersonCardProps): JSX.Element { - // e.g. https://swapi.dev/api/people/1 - const { isLoading, error, data } = useQuery('getPerson_' + person, () => - fetch('https://swapi.dev/api/people/' + person).then(res => + // api starts at 1 not 0 + const person = index+1; + + const { isLoading, error, data } = useQuery('getPerson_' + person, () => + fetch('https://swapi.dev/api/people/' + person).then(res => res.json() ) ); if (isLoading) return (

Loading...

); - if (error) return (

Error.

); + if (error) return (

Error fetching data.

); - // If request returns an empty response... - if (!data.name) { + if (!data?.name) { return ( @@ -45,19 +42,34 @@ function CharacterData({ person }: CharacterDataProps): JSX.Element { ); } - // Populate card with API data + const handleFavoriteClick = () => { + onFavoriteToggle(index); + }; + return ( - - - {data.name} - {data.height + ' cm, ' + data.mass + ' kg'} - - {data.name} has {data.eye_color} eyes, {data.hair_color} hair, and a {data.skin_color} skintone. - - Wikipedia - - + + + + {isFavorite ? 'Unfavorite' : 'Favorite'} + + {data.name} + + {data.height} cm, {data.mass} kg + + + {data.name} has {data.eye_color} eyes, {data.hair_color} hair, and a {data.skin_color} skintone. + + + Wikipedia + + + ); } -export default PersonCard; \ No newline at end of file +export default PersonCard; diff --git a/src/components/PersonView.tsx b/src/components/PersonView.tsx deleted file mode 100644 index a94bdf9..0000000 --- a/src/components/PersonView.tsx +++ /dev/null @@ -1,25 +0,0 @@ -interface PersonViewProps { - name: string, - height: number, - mass: number, - eye_color: string, - hair_color: string, - skin_color: string -} - -function PersonView({ name, height, mass, eye_color, hair_color, skin_color }: PersonViewProps) { - return ( -
-

{ name }

-
    -
  • height: { height }
  • -
  • mass: { mass }
  • -
  • eye_color: { eye_color }
  • -
  • hair_color: { hair_color }
  • -
  • skin_color: { skin_color }
  • -
-
- ) -} - -export default PersonView; \ No newline at end of file