diff --git a/src/App.tsx b/src/App.tsx index 09ace3c..521593b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,102 +1,38 @@ -// App.tsx -import { useState } from 'react'; -import { useCurMovies, usePopMovies } from './hooks/useMovies'; - +import { usePopMovies, useCurMovies } from './hooks/useMovies'; import { usePopularPeople } from './hooks/usePeople'; -//import { Button } from './components/Button'; -import { Sidebar} from './components/sidebar'; +import { Sidebar } from './components/sidebar'; +import { Section } from './components/Section'; +import MovieList from './components/MovieList'; +import { PersonList } from './components/PersonList'; import './App.css'; function App() { - const [searchQuery, setSearchQuery] = useState('') - //const [view, setView] = useState<'popular' | 'current'>('popular') - const popular = usePopMovies() const current = useCurMovies() const people = usePopularPeople() - const viewConfig = { - popular: { title: 'Populære filmer', query: popular }, - current: { title: 'Nye filmer', query: current }, - } -/* - const { title, query } = viewConfig[view] - const { data, isLoading, error } = query - - if (isLoading) { - return

Laster filmer...

- } - - if (error) { - return

Noe gikk galt: {error.message}

- } - - return ( -
-

{title}

- -
- ) - - -} -*/ - return (
- +
+
+ {popular.data && } +
-
-

Populære filmer

- {popular.isLoading &&

Laster filmer...

} - {popular.error &&

Noe gikk galt: {popular.error.message}

} - {popular.data && ( -
    - {popular.data.results.map((movie: any) => ( -
  • {movie.title}
  • - ))} -
- )} -
- -
-

Nye filmer

- {current.isLoading &&

Laster filmer...

} - {current.error &&

Noe gikk galt: {current.error.message}

} - {current.data && ( -
    - {current.data.results.map((movie: any) => ( -
  • {movie.title}
  • - ))} -
- )} -
+
+ {current.data && } +
-
-

Skuespillere

- {people.isLoading &&

Laster skuespillere...

} - {people.error &&

Noe gikk galt: {people.error.message}

} - {people.data && ( -
    - {people.data.results.map((person: any) => ( -
  • {person.name}
  • - ))} -
- )} -
+
+ {people.data && } +
+
- ) + ); } -export default App \ No newline at end of file +export default App; diff --git a/src/App.txt b/src/App.txt deleted file mode 100644 index 9ca8e8d..0000000 --- a/src/App.txt +++ /dev/null @@ -1,122 +0,0 @@ -import { useState } from 'react' -import heroImg from './assets/hero.png' -import reactLogo from './assets/react.svg' -import viteLogo from './assets/vite.svg' -import './App.css' - -function App() { - const [count, setCount] = useState(0) - - return ( - <> -
-
- - React logo - Vite logo -
-
-

Get started

-

- Edit src/App.tsx and save to test HMR -

-
- -
- -
- -
-
- -

Documentation

-

Your questions, answered

- -
-
- -

Connect with us

-

Join the Vite community

- -
-
- -
-
- - ) -} - -export default App diff --git a/src/api/tmdb.ts b/src/api/tmdb.ts index c298826..91d1e69 100644 --- a/src/api/tmdb.ts +++ b/src/api/tmdb.ts @@ -3,48 +3,32 @@ // får satt den opp senere. // BASE_URL er lenken vi bruker i en API request, her til TMDB -const TOKEN = import.meta.env.VITE_TMDB_TOKEN // v4 read access token -const BASE_URL = 'https://api.themoviedb.org/3' -export async function fetchPopularMovies() { - const res = await fetch( - `${BASE_URL}/movie/popular?language=en-US&page=1`, - { - headers: { - accept: 'application/json', - Authorization: `Bearer ${TOKEN}`, - }, - } - ) - if (!res.ok) throw new Error('Klarte ikke hente populære filmer...') - return res.json() +// TOKEN er lagret i .env.local, denne må alle legge til manuelt selv +import type { Movie, Person, TmdbResponse } from '../types/tmdb'; + +const TOKEN = import.meta.env.VITE_TMDB_TOKEN; // v4 read access token +const BASE_URL = 'https://api.themoviedb.org/3'; + +async function tmdbFetch(path: string, errorMessage: string): Promise { + const res = await fetch(`${BASE_URL}${path}`, { + headers: { + accept: 'application/json', + Authorization: `Bearer ${TOKEN}`, + }, + }); + if (!res.ok) throw new Error(errorMessage); + return res.json(); +} + +export function fetchPopularMovies(): Promise> { + return tmdbFetch('/movie/popular?language=en-US&page=1', 'Klarte ikke hente populære filmer...'); } -export async function fetchCurrentMovies() { - const res = await fetch( - `${BASE_URL}/movie/now_playing?language=en-US&page=1`, - { - headers: { - accept: 'application/json', - Authorization: `Bearer ${TOKEN}`, - }, - } - ) - if (!res.ok) throw new Error('Klarte ikke hente aktuelle filmer...') - return res.json() +export function fetchCurrentMovies(): Promise> { + return tmdbFetch('/movie/now_playing?language=en-US&page=1', 'Klarte ikke hente aktuelle filmer...'); } -//lagt til popular people -export async function fetchPopularPeople() { - const res = await fetch( - `${BASE_URL}/person/popular?language=en-US&page=1`, - { - headers: { - accept: 'application/json', - Authorization: `Bearer ${TOKEN}`, - }, - } - ) - if (!res.ok) throw new Error('Klarte ikke hente skuespillere...') - return res.json() +export function fetchPopularPeople(): Promise> { + return tmdbFetch('/person/popular?language=en-US&page=1', 'Klarte ikke hente skuespillere...'); } \ No newline at end of file diff --git a/src/components/MovieList.tsx b/src/components/MovieList.tsx index a12bf25..0bc4c80 100644 --- a/src/components/MovieList.tsx +++ b/src/components/MovieList.tsx @@ -1,41 +1,35 @@ // Henter inn MovieCard-komponenten vår, slik at vi kan bruke den til å vise hver film. import MovieCard from "./Movie_card"; -// Henter inn hooken som henter populære filmer fra TMDB via TanStack Query. -import { usePopMovies } from "../hooks/usePopMovies"; +// Typen for én film fra TMDB, så vi slipper any og får autocomplete på feltene. +import type { Movie } from "../types/tmdb"; -// MovieList har ansvaret for å hente filmene og vise dem som en liste av MovieCard. +// MovieList tar imot ferdig hentede filmer som prop og viser dem som en liste av MovieCard. +// Selve hentingen skjer via hookene i App.tsx, og Section håndterer laster- og feilmelding, +// så denne komponenten slipper å gjøre det samme en gang til. // MovieCard selv vet ingenting om TMDB eller fetching, den bare mottar props. -function MovieList() { - // useQuery (inni usePopMovies) returnerer et objekt med bl.a. data, isLoading og error. - // Vi destrukturerer ut akkurat de tre vi trenger her. - const { data, isLoading, error } = usePopMovies(); - - // Mens dataene fortsatt hentes fra API-et, viser vi en enkel lastetekst - // i stedet for en tom/ødelagt liste. - if (isLoading) { - return

Laster filmer...

; - } - - // Hvis noe gikk galt med kallet (f.eks. nettverksfeil eller ugyldig token), - // viser vi en enkel feilmelding i stedet for å krasje siden. - if (error) { - return

Klarte ikke hente filmer akkurat nå.

; - } +type MovieListProps = { + movies: Movie[]; +}; +function MovieList({ movies }: MovieListProps) { return ( // Ytre container for hele filmlisten, kan style'es med CSS senere
- {/* data er svaret fra TMDB, som har en "results"-liste med filmobjekter. - ?. brukes i tilfelle data enda ikke har kommet (undefined). */} - {data?.results.map((movie: any) => ( + {movies.map((movie) => ( // .map() går gjennom hver film og returnerer én MovieCard per film. // key={movie.id} er påkrevd av React for å holde styr på hvert element i lista. -export default MovieList; +export default MovieList; \ No newline at end of file diff --git a/src/components/PersonList.tsx b/src/components/PersonList.tsx new file mode 100644 index 0000000..bd732d1 --- /dev/null +++ b/src/components/PersonList.tsx @@ -0,0 +1,15 @@ +import type { Person } from '../types/tmdb'; + +type PersonListProps = { + people: Person[]; +}; + +export function PersonList({ people }: PersonListProps) { + return ( +
    + {people.map((person) => ( +
  • {person.name}
  • + ))} +
+ ); +} \ No newline at end of file diff --git a/src/components/Section.tsx b/src/components/Section.tsx new file mode 100644 index 0000000..f2f77ce --- /dev/null +++ b/src/components/Section.tsx @@ -0,0 +1,22 @@ +// denne filen eier overskrift + laster/feil-håndtering, tar innholdet som 'children': + +import type { ReactNode } from 'react'; + +type SectionProps = { + id: string; + title: string; + isLoading: boolean; + error: Error | null; + children: ReactNode; +}; + +export function Section({ id, title, isLoading, error, children }: SectionProps) { + return ( +
+

{title}

+ {isLoading &&

Laster...

} + {error &&

Noe gikk galt: {error.message}

} + {!isLoading && !error && children} +
+ ); +} \ No newline at end of file diff --git a/src/hooks/usePopMovies.ts b/src/hooks/usePopMovies.ts index 5321147..9fe33f4 100644 --- a/src/hooks/usePopMovies.ts +++ b/src/hooks/usePopMovies.ts @@ -1,6 +1,6 @@ // hooks/useMovies.ts import { useQuery } from '@tanstack/react-query' -import { fetchCurrentMovies, fetchPopularMovies } from '../api/tmdb' +import { fetchPopularMovies } from '../api/tmdb' export function usePopMovies() { return useQuery({ diff --git a/src/types/tmdb.ts b/src/types/tmdb.ts new file mode 100644 index 0000000..843b689 --- /dev/null +++ b/src/types/tmdb.ts @@ -0,0 +1,21 @@ +export type Movie = { + id: number; + title: string; + poster_path: string | null; + vote_average: number; + release_date: string; +}; + +export type Person = { + id: number; + name: string; + profile_path: string | null; +}; + +// TMDB pakker alltid svaret i samme struktur, uansett hva results inneholder +export type TmdbResponse = { + page: number; + results: T[]; + total_pages: number; + total_results: number; +}; \ No newline at end of file