diff --git a/movie-group-8/.env b/movie-group-8/.env new file mode 100644 index 0000000000000000000000000000000000000000..e847065da5446d61fa32a810397b21b7383e4cb1 --- /dev/null +++ b/movie-group-8/.env @@ -0,0 +1 @@ +VITE_TMDB_API_KEY=42259df77843511296d8096fa29e08a8 diff --git a/movie-group-8/src/components/Navbar.vue b/movie-group-8/src/components/Navbar.vue index 5ae04011c21832e5f613c42d75b1f64557a642b5..7abd7a88907732e7e5cb61faa5c7cbbd8d27570d 100644 --- a/movie-group-8/src/components/Navbar.vue +++ b/movie-group-8/src/components/Navbar.vue @@ -86,6 +86,8 @@ const userPhotoURL = ref(''); const navigation = ref([ { name: 'Films', href: '/films', authRequired: true }, { name: 'Watchlist', href: '/watchlist', authRequired: true }, + { name: 'Top Rated', href: '/top-rated', authRequired: true }, + ]); // Compute the current route to match against the navigation links diff --git a/movie-group-8/src/components/TopRatedMovies.vue b/movie-group-8/src/components/TopRatedMovies.vue new file mode 100644 index 0000000000000000000000000000000000000000..8b5bfc579781e6ae9467420581cd36848010a234 --- /dev/null +++ b/movie-group-8/src/components/TopRatedMovies.vue @@ -0,0 +1,140 @@ +<script setup> +import { ref, onMounted, watch } from 'vue' + +const year = ref(2000) +const genre = ref('') +const genres = ref([]) +const movies = ref([]) + +const TMDB_API_KEY = import.meta.env.VITE_TMDB_API_KEY + +// Fetch genres on component mount +onMounted(async () => { + const genreRes = await fetch(`https://api.themoviedb.org/3/genre/movie/list?api_key=${TMDB_API_KEY}`) + const genreData = await genreRes.json() + genres.value = genreData.genres +}) + +// Watch for changes in year or genre and fetch movies +watch([year, genre], async () => { + const url = `https://api.themoviedb.org/3/discover/movie?api_key=${TMDB_API_KEY}&sort_by=vote_average.desc&vote_count.gte=50&primary_release_year=${year.value}` + + (genre.value ? `&with_genres=${genre.value}` : '') + + const res = await fetch(url) + const data = await res.json() + movies.value = data.results +}, { immediate: true }) +</script> + +<template> + <div class="nyt-header"> + <h1>The Movies We've Loved Since 2000</h1> + <p> + Explore top-rated films using the filters below — by genre and year — and rediscover hidden gems. + </p> + </div> + + <div class="filters"> + <select v-model="genre"> + <option value="">Pick a genre ...</option> + <option v-for="g in genres" :key="g.id" :value="g.id">{{ g.name }}</option> + </select> + + <select v-model="year"> + <option v-for="y in Array.from({length: 25}, (_, i) => 2000 + i)" :key="y" :value="y"> + {{ y }} + </option> + </select> + </div> + + <h2 class="section-title">Our favorite movies from {{ year }}</h2> + + <div class="movie-grid"> + <div v-for="movie in movies" :key="movie.id" class="movie-card"> + <img + v-if="movie.poster_path" + :src="'https://image.tmdb.org/t/p/w342' + movie.poster_path" + alt="poster" + /> + <p class="title">{{ movie.title }}</p> + <p class="rating">â {{ movie.vote_average }}</p> + </div> + </div> + </template> + +<style scoped> +.nyt-header { + text-align: center; + max-width: 700px; + margin: 3rem auto 2rem auto; + padding: 0 1rem; +} +.nyt-header h1 { + font-size: 2.4rem; + font-weight: 700; + line-height: 1.2; +} +.nyt-header p { + font-size: 1.1rem; + margin-top: 0.75rem; + color: #555; +} + +.filters { + display: flex; + justify-content: center; + flex-wrap: wrap; + gap: 1rem; + margin-bottom: 2rem; +} + +.filters select { + padding: 0.5rem 1.2rem; + background-color: #cce8ff; + border: none; + border-radius: 8px; + font-weight: bold; + font-size: 1rem; + color: #003366; + cursor: pointer; + transition: background-color 0.2s ease; +} +.filters select:hover { + background-color: #b2dcff; +} + +.section-title { + text-align: center; + font-size: 1.6rem; + font-weight: bold; + margin-bottom: 1.5rem; +} + +.movie-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); + gap: 1.2rem; + padding: 0 1rem; +} + +.movie-card { + text-align: center; +} + +.movie-card img { + width: 100%; + border-radius: 12px; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); +} + +.title { + font-weight: 600; + margin-top: 0.5rem; +} + +.rating { + color: #ffa500; + font-size: 0.9rem; +} + +</style> diff --git a/movie-group-8/src/router/index.js b/movie-group-8/src/router/index.js index 21bc9eb618bc3e8241d221a76a4e894b022d6ca4..b8f7ba088c1142865c0ffda0982f458c8f7f6c5b 100644 --- a/movie-group-8/src/router/index.js +++ b/movie-group-8/src/router/index.js @@ -1,10 +1,15 @@ import { getAuth, onAuthStateChanged } from 'firebase/auth'; import { createRouter, createWebHistory } from 'vue-router'; + + const router = createRouter({ history: createWebHistory(), routes: [ + + { path: '/', component: () => import('../views/Home.vue') }, + {path: '/top-rated', component: () => import('../views/TopRated.vue'), meta: { requiresAuth: true }}, { path: '/register', component: () => import('../views/Register.vue') }, { path: '/login', component: () => import('../views/Login.vue') }, { path: '/recover-account', component: () => import('../views/RecoverAccount.vue') }, diff --git a/movie-group-8/src/views/Films.vue b/movie-group-8/src/views/Films.vue index 072b643deb3df336d3dd52bd1cccf947486db309..3f3aee48247556da2a8e6c5b0598ebc70359d515 100644 --- a/movie-group-8/src/views/Films.vue +++ b/movie-group-8/src/views/Films.vue @@ -1 +1,8 @@ -<template>Films</template> \ No newline at end of file +<script setup> +import TopRatedMovies from '@/components/TopRatedMovies.vue' +</script> + +<template> + <TopRatedMovies /> +</template> + diff --git a/movie-group-8/src/views/TopRated.vue b/movie-group-8/src/views/TopRated.vue new file mode 100644 index 0000000000000000000000000000000000000000..a8e7c29534e976e832e311de90f8230cd613b57a --- /dev/null +++ b/movie-group-8/src/views/TopRated.vue @@ -0,0 +1,44 @@ +<script setup> +import TopRatedMovies from '@/components/TopRatedMovies.vue' + +</script> + +<template> + <TopRatedMovies /> + <template> + <div class="nyt-header"> + <h1>The Movies We've Loved Since 2000</h1> + <p> + Explore top-rated films using the filters below — by genre and year — and rediscover hidden gems. + </p> + </div> + + <div class="filters"> + <select v-model="genre"> + <option value="">Pick a genre ...</option> + <option v-for="g in genres" :key="g.id" :value="g.id">{{ g.name }}</option> + </select> + + <select v-model="year"> + <option v-for="y in Array.from({length: 25}, (_, i) => 2000 + i)" :key="y" :value="y"> + {{ y }} + </option> + </select> + </div> + + <h2 class="section-title">Our favorite movies from {{ year }}</h2> + + <div class="movie-grid"> + <div v-for="movie in movies" :key="movie.id" class="movie-card"> + <img + v-if="movie.poster_path" + :src="'https://image.tmdb.org/t/p/w342' + movie.poster_path" + alt="poster" + /> + <p class="title">{{ movie.title }}</p> + <p class="rating">â {{ movie.vote_average }}</p> + </div> + </div> +</template> + +</template> diff --git a/movie-group-8/vite.config.js b/movie-group-8/vite.config.js index daa46667b4af838f364c216285fc6f9beee013be..8bbf4703424d6582b10489e90bdad6599bfa8345 100644 --- a/movie-group-8/vite.config.js +++ b/movie-group-8/vite.config.js @@ -1,10 +1,18 @@ import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import tailwindcss from '@tailwindcss/vite' +import path from 'path' + // https://vite.dev/config/ export default defineConfig({ plugins: [vue(), tailwindcss() ], + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + }, + })