From 3f6f885dbd1df025d414475a8248a0bc662f016d Mon Sep 17 00:00:00 2001 From: albdert Date: Mon, 14 Sep 2026 16:33:21 +0200 Subject: [PATCH 1/4] app.txs restructure --- src/App.tsx | 2 +- src/components/TrendingPreview.css | 2 +- src/components/TrendingPreview.tsx | 10 +++++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index e628e9c..e45e7b2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,8 +6,8 @@ import { Section } from './components/Section'; import { MovieList } from './components/MovieList'; import { PersonList } from './components/PersonList'; import { useDebouncedValue } from './hooks/useDebouncedValue'; -import './App.css'; import { TrendingPreview } from './components/TrendingPreview'; +import './App.css'; function App() { diff --git a/src/components/TrendingPreview.css b/src/components/TrendingPreview.css index 32702d2..ad1012b 100644 --- a/src/components/TrendingPreview.css +++ b/src/components/TrendingPreview.css @@ -20,7 +20,7 @@ color: white; margin: 0; z-index: 2; - cursor: pointer; + font-size: clamp(1.5rem, 4vh, 3rem); } .hero-track { diff --git a/src/components/TrendingPreview.tsx b/src/components/TrendingPreview.tsx index ae18d56..98ffda8 100644 --- a/src/components/TrendingPreview.tsx +++ b/src/components/TrendingPreview.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; import type { Movie } from "../types/tmdb"; import "./TrendingPreview.css" @@ -27,6 +27,14 @@ export function TrendingPreview({ movies }: TrendingPreviewProps) { }, 400); } + useEffect(() => { + const timer = setInterval(() => { + handleClick(1); + }, 5000); + + return () => clearInterval(timer); + }, [index, transitioning]); + function openDetails() { return; } From 8196139fa068fa8aeca27961a3e25e6fdb690a3f Mon Sep 17 00:00:00 2001 From: albdert Date: Mon, 14 Sep 2026 19:09:00 +0200 Subject: [PATCH 2/4] tmp, fix sidebar, moviecard, movielist, trendingpreview --- src/App.tsx | 18 ++++- src/components/MovieCard.tsx | 32 +++++--- src/components/MovieDetails.css | 45 +++++++++++ src/components/MovieDetails.tsx | 43 ++++++++++ src/components/MovieList.tsx | 29 +++---- src/components/TrendingPreview.css | 3 +- src/components/TrendingPreview.tsx | 15 ++-- src/hooks/bak.usePopMovies | 10 --- src/tmp.txt | 122 ----------------------------- 9 files changed, 145 insertions(+), 172 deletions(-) create mode 100644 src/components/MovieDetails.css create mode 100644 src/components/MovieDetails.tsx delete mode 100644 src/hooks/bak.usePopMovies delete mode 100644 src/tmp.txt diff --git a/src/App.tsx b/src/App.tsx index 3ef9071..4076dac 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -8,11 +8,16 @@ import { PersonList } from './components/PersonList'; import { useDebouncedValue } from './hooks/useDebouncedValue'; import { useFavorites } from './hooks/useFavorites'; import { TrendingPreview } from './components/TrendingPreview'; +import { MovieDetails } from './components/MovieDetails'; + +import type { Movie } from './types/tmdb'; + import './App.css'; function App() { const [query, setQuery] = useState(''); + const [selectedMovie, setSelectedMovie] = useState(null); const popular = usePopMovies() const current = useCurMovies() @@ -29,19 +34,20 @@ function App() {
{isSearching ? (
- {search.data && } + {search.data && }
) : ( <>
- {current.data && } + {current.data && }
+
- {popular.data && } + {popular.data && }
- {current.data && } + {current.data && }
@@ -51,6 +57,10 @@ function App() { )}
+ {selectedMovie && ( + setSelectedMovie(null)} /> + )} + ); diff --git a/src/components/MovieCard.tsx b/src/components/MovieCard.tsx index 527203a..93e7193 100644 --- a/src/components/MovieCard.tsx +++ b/src/components/MovieCard.tsx @@ -1,8 +1,10 @@ import { useMovieCredits } from "../hooks/useMovieCredits"; +import type { Movie } from "../types/tmdb"; import "./MovieCard.css"; // Definerer hvilke props (data utenfra) komponenten forventer å motta, // og hvilken type hver av dem skal ha. +/* interface MovieCardProps { movieId: number; // TMDB sin filmid, brukes til å hente skuespillere title: string; // Filmens tittel, skal vises som overskrift @@ -10,36 +12,46 @@ interface MovieCardProps { rating: number; // Terningkast/prosent (0-100), f.eks. vote_average * 10 fra TMDB isFavorite: boolean; // Om denne filmen er markert som favoritt onToggleFavorite: (id: number) => void; // Kalles når stjernen klikkes + onSelectMovie: (movie: Movie) => void; } +*/ -function MovieCard({ movieId, title, posterUrl, rating, isFavorite, onToggleFavorite }: MovieCardProps) { +interface MovieCardProps { + movie: Movie; + isFavorite: boolean; // Om denne filmen er markert som favoritt + onToggleFavorite: (id: number) => void; // Kalles når stjernen klikkes + onSelectMovie: (movie: Movie) => void; +} + +function MovieCard({ movie, isFavorite, onToggleFavorite, onSelectMovie }: MovieCardProps) { // Henter skuespillere for akkurat denne filmen når kortet vises - const { data: credits, isLoading } = useMovieCredits(movieId); + const { data: credits, isLoading } = useMovieCredits(movie.id); // Viser kun de 3 første, sortert etter TMDB sin billed-rekkefølge const topActors = credits?.cast.slice(0, 3) ?? []; + // TMDB sin vote_average går fra 0-10, vi ganger med 10 for å få prosent (0-100) + const rating = Math.round(movie.vote_average * 10); + const path = movie.poster_path?`https://image.tmdb.org/t/p/w200${movie.poster_path}`:""; return ( -
+
{onSelectMovie(movie) }}> {rating}% - {title} + {movie.title}
-

{title}

- - {isLoading &&

Laster skuespillere...

} - +

{movie.title}

+ {isLoading &&

Laster skuespillere...

} {!isLoading && topActors.length > 0 && (

{topActors.map((actor) => actor.name).join(", ")} diff --git a/src/components/MovieDetails.css b/src/components/MovieDetails.css new file mode 100644 index 0000000..f91dc5f --- /dev/null +++ b/src/components/MovieDetails.css @@ -0,0 +1,45 @@ +.details-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.7); + display: flex; + align-items: center; + justify-content: center; + z-index: 999; +} + + +.details-card { + position: relative; + width: 60vw; + max-width: 800px; + max-height: 80vh; + overflow-y: auto; + background: #111; + border-radius: 1rem; + color: white; +} + +.details-backdrop { + width: 100%; + height: 300px; + object-fit: cover; + border-radius: 1rem 1rem 0 0; +} + +.details-close { + position: absolute; + top: 1rem; + right: 1rem; + background: rgba(0, 0, 0, 0.5); + color: white; + border: none; + border-radius: 50%; + width: 2rem; + height: 2rem; + cursor: pointer; +} + +.details-content { + padding: 1.5rem; +} \ No newline at end of file diff --git a/src/components/MovieDetails.tsx b/src/components/MovieDetails.tsx new file mode 100644 index 0000000..c535a74 --- /dev/null +++ b/src/components/MovieDetails.tsx @@ -0,0 +1,43 @@ +import { useMovieCredits } from "../hooks/useMovieCredits"; +import type { Movie } from "../types/tmdb"; +import "./MovieDetails.css" + +type MovieDetailsProps = { + movie: Movie; + onClose: () => void; +}; + + + +export function MovieDetails({ movie, onClose }: MovieDetailsProps) { + + const { data: credits, isLoading } = useMovieCredits(movie.id); + + return ( +

+
e.stopPropagation()}> + {movie.title} + + + +
+

{movie.title}

+

+ {movie.release_date} · ★ {movie.vote_average.toFixed(1)} +

+

{movie.overview}

+
+
+ {isLoading &&

Laster skuespillere...

} + {credits?.cast.slice(0, 6).map((actor) => ( + {actor.name} + ))} +
+
+
+ ); +} \ No newline at end of file diff --git a/src/components/MovieList.tsx b/src/components/MovieList.tsx index fa9e86a..5af9167 100644 --- a/src/components/MovieList.tsx +++ b/src/components/MovieList.tsx @@ -11,35 +11,26 @@ type MovieListProps = { movies: Movie[]; isFavorite: (id: number) => boolean; onToggleFavorite: (id: number) => void; + onSelectMovie: (movie: Movie) => void; }; -export function MovieList({ movies, isFavorite, onToggleFavorite }: MovieListProps) { +export function MovieList({ movies, isFavorite, onToggleFavorite, onSelectMovie }: MovieListProps) { return ( // Ytre container for hele filmlisten, kan style'es med CSS senere
{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. - + /* isFavorite={isFavorite(movie.id)} onToggleFavorite={onToggleFavorite} - /> + */ ))}
); diff --git a/src/components/TrendingPreview.css b/src/components/TrendingPreview.css index ad1012b..0ef0e66 100644 --- a/src/components/TrendingPreview.css +++ b/src/components/TrendingPreview.css @@ -1,7 +1,8 @@ .hero { position: relative; + /* skal dekke 100% av bredden og ha konstant høyde*/ width: 100%; - height: 30vh; /* adjust to taste, or set a fixed px value */ + height: 30vh; overflow: hidden; border-radius: 1rem; } diff --git a/src/components/TrendingPreview.tsx b/src/components/TrendingPreview.tsx index 98ffda8..6ad7e9f 100644 --- a/src/components/TrendingPreview.tsx +++ b/src/components/TrendingPreview.tsx @@ -2,11 +2,14 @@ import { useEffect, useState } from "react"; import type { Movie } from "../types/tmdb"; import "./TrendingPreview.css" +const tmdbURL = `https://image.tmdb.org/t/p/original`; + type TrendingPreviewProps = { movies : Movie[]; + onSelectMovie: (movie: Movie) => void; }; -export function TrendingPreview({ movies }: TrendingPreviewProps) { +export function TrendingPreview({ movies, onSelectMovie }: TrendingPreviewProps) { const [index, setIndex] = useState(0); const [offset, setOffset] = useState(0); const [transitioning, setTransitioning] = useState(false); @@ -49,26 +52,26 @@ export function TrendingPreview({ movies }: TrendingPreviewProps) { }} >
-

{movie.title}

+

onSelectMovie(movie)}>{movie.title}

handleClick(-1)}> {"❮"}
-
+
onSelectMovie(movie)} />
handleClick(1)}> {"❯"} diff --git a/src/hooks/bak.usePopMovies b/src/hooks/bak.usePopMovies deleted file mode 100644 index 9fe33f4..0000000 --- a/src/hooks/bak.usePopMovies +++ /dev/null @@ -1,10 +0,0 @@ -// hooks/useMovies.ts -import { useQuery } from '@tanstack/react-query' -import { fetchPopularMovies } from '../api/tmdb' - -export function usePopMovies() { - return useQuery({ - queryKey: ['movies', 'popular'], - queryFn: fetchPopularMovies, - }) -} \ No newline at end of file diff --git a/src/tmp.txt b/src/tmp.txt deleted file mode 100644 index 9ca8e8d..0000000 --- a/src/tmp.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 From cfa33d9fd4a3405e72f13743ba4e7237470e9436 Mon Sep 17 00:00:00 2001 From: albdert Date: Mon, 14 Sep 2026 21:49:35 +0200 Subject: [PATCH 3/4] mange fikser --- src/App.tsx | 2 +- src/components/FavoriteButton.css | 9 +++++ src/components/FavoriteButton.tsx | 27 ++++++++++++++ src/components/MovieCard.css | 9 +---- src/components/MovieCard.tsx | 11 +----- src/components/MovieDetails.css | 36 +++++++++++++++++- src/components/MovieDetails.tsx | 30 ++++++++++----- src/components/TrendingPreview.tsx | 4 -- src/components/sidebar.tsx | 59 ++++++++++++++++++------------ 9 files changed, 131 insertions(+), 56 deletions(-) create mode 100644 src/components/FavoriteButton.css create mode 100644 src/components/FavoriteButton.tsx diff --git a/src/App.tsx b/src/App.tsx index 57cd25e..198c38f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -71,7 +71,7 @@ function App() { {selectedMovie && ( - setSelectedMovie(null)} /> + setSelectedMovie(null)} /> )}
diff --git a/src/components/FavoriteButton.css b/src/components/FavoriteButton.css new file mode 100644 index 0000000..6c48dc5 --- /dev/null +++ b/src/components/FavoriteButton.css @@ -0,0 +1,9 @@ +.favorite-button { + background: none; + border: none; + padding: 0; + cursor: pointer; + line-height: 1; + font-size: 1.5rem; + color: #fbc02d; +} \ No newline at end of file diff --git a/src/components/FavoriteButton.tsx b/src/components/FavoriteButton.tsx new file mode 100644 index 0000000..75d2313 --- /dev/null +++ b/src/components/FavoriteButton.tsx @@ -0,0 +1,27 @@ +import type { Movie } from "../types/tmdb"; + +import "./FavoriteButton.css"; + +type FavoriteButtonProps = { + movie: Movie; + className?: string; + isFavorite: boolean; + onToggleFavorite: (id: number) => void; +}; + +export function FavoriteButton({ movie, className, isFavorite, onToggleFavorite }: FavoriteButtonProps) { + return ( + + ); +} \ No newline at end of file diff --git a/src/components/MovieCard.css b/src/components/MovieCard.css index 0e7996b..0cfb3af 100644 --- a/src/components/MovieCard.css +++ b/src/components/MovieCard.css @@ -89,13 +89,6 @@ bottom: 0.5rem; right: 0.5rem; z-index: 1; - background: none; - border: none; - padding: 0; - font-size: 1.5rem; - line-height: 1; - color: #fbc02d; - cursor: pointer; } @media (max-width: 480px) { @@ -110,4 +103,4 @@ .movie-card__info { padding-right: 3rem; } -} +} \ No newline at end of file diff --git a/src/components/MovieCard.tsx b/src/components/MovieCard.tsx index 93e7193..7396674 100644 --- a/src/components/MovieCard.tsx +++ b/src/components/MovieCard.tsx @@ -1,5 +1,6 @@ import { useMovieCredits } from "../hooks/useMovieCredits"; import type { Movie } from "../types/tmdb"; +import { FavoriteButton } from "./FavoriteButton"; import "./MovieCard.css"; // Definerer hvilke props (data utenfra) komponenten forventer å motta, @@ -35,15 +36,7 @@ function MovieCard({ movie, isFavorite, onToggleFavorite, onSelectMovie }: Movie return (
{onSelectMovie(movie) }}> - + {rating}% diff --git a/src/components/MovieDetails.css b/src/components/MovieDetails.css index f91dc5f..9b8c276 100644 --- a/src/components/MovieDetails.css +++ b/src/components/MovieDetails.css @@ -41,5 +41,39 @@ } .details-content { - padding: 1.5rem; + padding: 1.25rem 1.5rem; + text-align: center; +} + +.details-content h2 { + margin: 0 0 0.25rem; +} + +.details-meta { + margin: 0 0 1rem; + padding-bottom: 1rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.15); + color: rgba(255, 255, 255, 0.7); +} + +.details-content h3 { + margin: 1rem 0 0.5rem; + font-size: 1rem; +} + +.details-overview { + margin: 0 0 0.5rem; + line-height: 1.4; +} + +.details-actors { + margin: 0; +} + +/* MovieDetails.css */ +.details-favorite { + position: absolute; + top: 1rem; + left: 1rem; + z-index: 1; } \ No newline at end of file diff --git a/src/components/MovieDetails.tsx b/src/components/MovieDetails.tsx index c535a74..5da3174 100644 --- a/src/components/MovieDetails.tsx +++ b/src/components/MovieDetails.tsx @@ -1,15 +1,16 @@ import { useMovieCredits } from "../hooks/useMovieCredits"; import type { Movie } from "../types/tmdb"; +import { FavoriteButton } from "./FavoriteButton"; import "./MovieDetails.css" type MovieDetailsProps = { movie: Movie; + isFavorite: boolean; + onToggleFavorite: (id: number) => void; onClose: () => void; }; - - -export function MovieDetails({ movie, onClose }: MovieDetailsProps) { +export function MovieDetails({ movie, isFavorite, onToggleFavorite, onClose }: MovieDetailsProps) { const { data: credits, isLoading } = useMovieCredits(movie.id); @@ -24,18 +25,29 @@ export function MovieDetails({ movie, onClose }: MovieDetailsProps) { + +

{movie.title}

{movie.release_date} · ★ {movie.vote_average.toFixed(1)}

+ +

Sammendrag

{movie.overview}

-
-
- {isLoading &&

Laster skuespillere...

} - {credits?.cast.slice(0, 6).map((actor) => ( - {actor.name} - ))} + +

Skuespillere

+
+ {isLoading &&

Laster skuespillere...

} + {credits && ( +

{credits.cast.slice(0, 6).map((actor) => actor.name).join(", ")}

+ )} +
diff --git a/src/components/TrendingPreview.tsx b/src/components/TrendingPreview.tsx index 6ad7e9f..ef53660 100644 --- a/src/components/TrendingPreview.tsx +++ b/src/components/TrendingPreview.tsx @@ -38,10 +38,6 @@ export function TrendingPreview({ movies, onSelectMovie }: TrendingPreviewProps) return () => clearInterval(timer); }, [index, transitioning]); - function openDetails() { - return; - } - return (
void; @@ -8,38 +8,49 @@ type SidebarProps = { //under kommer seksjonene i midtkolonnen som sidebaren skal kunne hoppe mellom const sections = [ - { id: "populaere", label: "Populære"}, - { id: "nye-filmer", label: "Nye Filmer"}, - { id: "skuespillere", label: "Skuespillere"}, + { id: "populaere", label: "Populære" }, + { id: "nye-filmer", label: "Nye Filmer" }, + { id: "skuespillere", label: "Skuespillere" }, ]; -export function Sidebar({onSearch, searchValue}: SidebarProps) { +export function Sidebar({ onSearch, searchValue }: SidebarProps) { return ( - -