diff --git a/src/app/api/movies/route.js b/src/app/api/movies/route.js
index 8da3a51..e96f428 100644
--- a/src/app/api/movies/route.js
+++ b/src/app/api/movies/route.js
@@ -1,14 +1,28 @@
import { NextResponse } from 'next/server'
import connectDB from '@/lib/db/connectDB'
-import { getAllMovies, createMovieFromOmdbTitle, findMovieByTitle } from '@/lib/db/movieDbService'
+import { getAllMovies, createMovieFromOmdbTitle, findMovieByTitle, findMoviesByTitle } from '@/lib/db/movieDbService'
import { requireAdminAccess } from '@/lib/auth/requireAdminAccess'
-export async function GET() {
+
+export async function GET(req) {
await connectDB()
- const movies = await getAllMovies()
- return NextResponse.json(movies)
-}
+ //Backend search logic
+ const { searchParams } = new URL(req.url)
+ const query = searchParams.get('q')
+
+ try {
+ if (query) {
+ const result = await findMoviesByTitle(query)
+ return NextResponse.json(result)
+ }
+
+ const movies = await getAllMovies()
+ return NextResponse.json(movies)
+ } catch (error) {
+ return NextResponse.json({ error: error.message }, { status: 500 })
+ }
+}
export async function POST(req) {
if (requireAdminAccess()) {
return NextResponse.json({ error: 'Endast tillgängligt för administratörer' }, { status: 403 })
diff --git a/src/components/SearchField.jsx b/src/components/SearchField.jsx
index 5819776..b500bd4 100644
--- a/src/components/SearchField.jsx
+++ b/src/components/SearchField.jsx
@@ -1,11 +1,72 @@
'use client'
+import { useState, useEffect } from 'react'
+
+//Updating the SearchField to handle a backend search logic
export default function SearchField() {
+ const [query, setQuery] = useState('')
+ const [results, setResults] = useState([])
+
+ const handleSearch = async () => {
+ if (query.trim() === '') return
+ try {
+ const res = await fetch(`/api/movies?q=${encodeURIComponent(query)}`)
+ if (!res.ok) throw new Error('Något gick fel vid sökning')
+ const data = await res.json()
+ setResults(data)
+ } catch (error) {
+ console.error('Sökfel:', error)
+ setResults([])
+ }
+ }
+
+ //Debounce for UX improvements
+ useEffect(() => {
+ const timeoutId = setTimeout(() => {
+ handleSearch()
+ }, 500)
+
+ return () => clearTimeout(timeoutId)
+ }, [query])
+
return (
<>
>
)
diff --git a/src/lib/db/movieDbService.js b/src/lib/db/movieDbService.js
index e000bad..482de4e 100644
--- a/src/lib/db/movieDbService.js
+++ b/src/lib/db/movieDbService.js
@@ -56,3 +56,16 @@ export async function createMovieFromOmdbTitle(title) {
return await movie.save()
}
+
+//MongoDB searches all titles that contains the query, no matter if upper or lower case
+function escapeRegex(string) {
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+}
+
+export async function findMoviesByTitle(query) {
+ await connectDB()
+ const safeQuery = escapeRegex(query)
+ return await Movie.find({
+ title: { $regex: safeQuery, $options: 'i' },
+ }).lean() //Lean improves performance, returns JS objects instead of mongoose documents.
+}
diff --git a/src/styles/MovieCard.scss b/src/styles/MovieCard.scss
index 4c55491..ff658fe 100644
--- a/src/styles/MovieCard.scss
+++ b/src/styles/MovieCard.scss
@@ -98,3 +98,36 @@
text-align: center;
}
}
+
+.moviecard__list-search {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
+ gap: 1rem;
+ margin-top: 1rem;
+}
+
+.moviecard__container-search {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+ background: #f2f2f2;
+ padding: 1rem;
+ border-radius: 8px;
+ transition: transform 0.2s ease;
+}
+
+.moviecard__container-search:hover {
+ transform: scale(1.05);
+}
+
+.moviecard__poster-search {
+ width: 100%;
+ height: auto;
+ border-radius: 4px;
+}
+
+.moviecard__title-search {
+ margin-top: 0.5rem;
+ font-weight: bold;
+}
diff --git a/src/styles/moviePage.scss b/src/styles/moviePage.scss
index e1c81b8..6b1194e 100644
--- a/src/styles/moviePage.scss
+++ b/src/styles/moviePage.scss
@@ -36,6 +36,22 @@
}
}
+.search-button {
+ background-color: #03658c;
+ color: white;
+ border: none;
+ padding: 0.5rem 1rem;
+ margin-left: 0.2rem;
+ border-radius: 6px;
+ font-weight: bold;
+ cursor: pointer;
+ transition: background-color 0.2s ease;
+}
+
+.search-button:hover {
+ background-color: #02496a;
+}
+
.moviecard__list {
list-style: none;
diff --git a/src/tests/api/movies/movies.test.js b/src/tests/api/movies/movies.test.js
index 9eac272..2d3b2ea 100644
--- a/src/tests/api/movies/movies.test.js
+++ b/src/tests/api/movies/movies.test.js
@@ -1,7 +1,7 @@
// src/tests/admin/admin.test.js
import { FormData } from 'formdata-node'
import { expect, jest, test, describe, beforeEach, beforeAll } from '@jest/globals'
-import { findMovieById } from '@/lib/db/movieDbService'
+import { findMoviesByTitle } from '@/lib/db/movieDbService'
// Mock database and services
jest.unstable_mockModule('@/lib/db/connectDB', () => ({
@@ -16,6 +16,7 @@ jest.unstable_mockModule('@/lib/db/movieDbService', () => ({
getAllMovies: jest.fn(),
deleteMovieAndScreenings: jest.fn(),
findMovieById: jest.fn(),
+ findMoviesByTitle: jest.fn(),
}))
let POST, GET, DELETE
@@ -93,7 +94,8 @@ describe('GET /api/movies (mocked)', () => {
const mockMovies = [{ _id: '1', title: 'Interstellar' }]
movieService.getAllMovies.mockResolvedValue(mockMovies)
- const res = await GET()
+ const req = { url: 'http://localhost/api/movies' }
+ const res = await GET(req)
const data = await res.json()
expect(res.status).toBe(200)
diff --git a/src/tests/api/rooms/rooms.test.js b/src/tests/api/rooms/rooms.test.js
index 2d6fe6d..84451d7 100644
--- a/src/tests/api/rooms/rooms.test.js
+++ b/src/tests/api/rooms/rooms.test.js
@@ -1,4 +1,3 @@
-import { getRoomById } from '@/lib/db/roomDbService'
import { expect, jest, test, describe, beforeAll, beforeEach } from '@jest/globals'
jest.unstable_mockModule('@/lib/db/connectDB', () => ({
diff --git a/src/tests/api/screenings/screenings.test.js b/src/tests/api/screenings/screenings.test.js
index 80cf33a..bbd644e 100644
--- a/src/tests/api/screenings/screenings.test.js
+++ b/src/tests/api/screenings/screenings.test.js
@@ -1,7 +1,6 @@
// src/tests/screenings/screenings.test.js
import { FormData } from 'formdata-node'
import { expect, jest, test, describe, beforeEach, beforeAll } from '@jest/globals'
-import { getScreeningById } from '@/lib/db/screeningDbService'
// Mock database and services
jest.unstable_mockModule('@/lib/db/connectDB', () => ({