🧱 added backend
Co-authored-by: haraldnilsen <harald_998@hotmail.com>
This commit is contained in:
parent
cd2dbd6eee
commit
a260cb63b7
29 changed files with 16 additions and 4 deletions
65
frontend/src/components/MovieForm/index.tsx
Normal file
65
frontend/src/components/MovieForm/index.tsx
Normal file
|
@ -0,0 +1,65 @@
|
|||
import q from 'qjuul'
|
||||
|
||||
interface MovieFormProps {
|
||||
handleMovieSubmit: (event: any) => void
|
||||
setMovieTitle: (title: string) => void
|
||||
setMovieYear: (year: string) => void
|
||||
setMovieType: (type: string) => void
|
||||
movieTitle: string
|
||||
}
|
||||
|
||||
const MovieForm: React.FC<MovieFormProps> = ({
|
||||
handleMovieSubmit,
|
||||
setMovieTitle,
|
||||
setMovieType,
|
||||
setMovieYear,
|
||||
movieTitle,
|
||||
}) => {
|
||||
return (
|
||||
<q.form
|
||||
className="flex flex-col gap-3 card p-4 rounded-lg w-full lg:px-14"
|
||||
onSubmit={(event) => handleMovieSubmit({ event: event })}
|
||||
>
|
||||
<q.label>Choose a movie title:</q.label>
|
||||
<q.input
|
||||
type="text"
|
||||
id="movieTitle"
|
||||
placeholder="Movie title"
|
||||
value={movieTitle.length > 0 ? movieTitle : ''}
|
||||
onChange={(e) => setMovieTitle(e.target.value)}
|
||||
className="border text-black border-gray-300 rounded-md p-2 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:border-transparent"
|
||||
/>
|
||||
<q.label>Choose year movie was made: (OPTIONAL)</q.label>
|
||||
<q.select
|
||||
className="p-2 rounded-md border text-gray-500 border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:border-transparent"
|
||||
onChange={(e) => setMovieYear(e.target.value)}
|
||||
>
|
||||
{/* Option for year 1923-2023 */}
|
||||
<q.option value="">All years</q.option>
|
||||
{Array.from(Array(100), (e, i) => {
|
||||
const year = 2023 - i
|
||||
return (
|
||||
<q.option key={i} value={year}>
|
||||
{year}
|
||||
</q.option>
|
||||
)
|
||||
})}
|
||||
</q.select>
|
||||
<q.label>Choose type: (OPTIONAL)</q.label>
|
||||
<q.select
|
||||
className="p-2 rounded-md border text-gray-500 border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:border-transparent"
|
||||
onChange={(e) => setMovieType(e.target.value)}
|
||||
>
|
||||
<q.option value="">All types</q.option>
|
||||
<q.option value="movie">Movies</q.option>
|
||||
<q.option value="series">Series</q.option>
|
||||
<q.option value="episode">Episodes</q.option>
|
||||
</q.select>
|
||||
<q.button className="bg-white p-3 my-2 rounded-md w-3/5 md:w-2/5 mx-auto text-red-900 font-semibold">
|
||||
Find movies
|
||||
</q.button>
|
||||
</q.form>
|
||||
)
|
||||
}
|
||||
|
||||
export default MovieForm
|
45
frontend/src/components/MovieModal/index.tsx
Normal file
45
frontend/src/components/MovieModal/index.tsx
Normal file
|
@ -0,0 +1,45 @@
|
|||
import type { modalMovieType } from '../../types/movie'
|
||||
import q from 'qjuul'
|
||||
import Modal from 'react-modal'
|
||||
|
||||
interface MovieModalProps {
|
||||
setModalOpen: (status: boolean) => void
|
||||
modalMovie: modalMovieType
|
||||
modalOpen: boolean
|
||||
}
|
||||
|
||||
const MovieModal: React.FC<MovieModalProps> = ({
|
||||
setModalOpen,
|
||||
modalMovie,
|
||||
modalOpen,
|
||||
}) => {
|
||||
return (
|
||||
<Modal
|
||||
className="p-6 bg-black rounded-lg outline-none" // styles for the modal container
|
||||
overlayClassName="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center" // styles for the overlay
|
||||
isOpen={modalOpen}
|
||||
onRequestClose={() => setModalOpen(false)}
|
||||
>
|
||||
<q.div className="flex">
|
||||
<q.img
|
||||
className="h-44 max-w-sm mx-auto mb-4 rounded-md shadow"
|
||||
src={modalMovie?.Poster}
|
||||
alt={modalMovie?.Title}
|
||||
/>
|
||||
<q.div className="flex flex-col px-10 pt-5">
|
||||
<q.h1 className="text-xl font-bold mb-4">{modalMovie?.Title}</q.h1>
|
||||
<q.p>Year: {modalMovie?.Year}</q.p>
|
||||
<q.p>Type: {modalMovie?.Type}</q.p>
|
||||
<q.a
|
||||
className='"underline text-blue-500 hover:text-blue-700"'
|
||||
href={`https://www.imdb.com/title/${modalMovie?.imdbID}`}
|
||||
>
|
||||
{`https://www.imdb.com/title/${modalMovie?.imdbID}`}
|
||||
</q.a>
|
||||
</q.div>
|
||||
</q.div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default MovieModal
|
57
frontend/src/components/MovieTable/index.tsx
Normal file
57
frontend/src/components/MovieTable/index.tsx
Normal file
|
@ -0,0 +1,57 @@
|
|||
import type { movieObject } from '../../types/movie'
|
||||
import q from 'qjuul'
|
||||
import MovieTableRow from '../MovieTableRow'
|
||||
import arrow from '../../../public/arrow-down-up-svgrepo-com.svg'
|
||||
|
||||
interface MovieTableProps {
|
||||
handleModalOpen: (movie: movieObject) => void
|
||||
sortHandler: (sortType: string) => void
|
||||
movies: movieObject[]
|
||||
}
|
||||
|
||||
const MovieTable: React.FC<MovieTableProps> = ({
|
||||
handleModalOpen,
|
||||
sortHandler,
|
||||
movies,
|
||||
}) => {
|
||||
return (
|
||||
<q.div className="w-full">
|
||||
<q.table className="border-separate border-spacing-y-5 w-full">
|
||||
<q.thead className="">
|
||||
<q.tr>
|
||||
<q.th className="text-left"></q.th>
|
||||
<q.th
|
||||
className=" text-left hover:cursor-pointer"
|
||||
onClick={() => sortHandler('title')}
|
||||
>
|
||||
<q.div className="flex font-bold">
|
||||
Title
|
||||
<q.img src={arrow} className="w-3 ml-1 " />
|
||||
</q.div>
|
||||
</q.th>
|
||||
<q.th
|
||||
className="text-left hover:cursor-pointer"
|
||||
onClick={() => sortHandler('year')}
|
||||
>
|
||||
<q.div className="flex">
|
||||
Year
|
||||
<q.img src={arrow} className="w-3 ml-1" />
|
||||
</q.div>
|
||||
</q.th>
|
||||
</q.tr>
|
||||
</q.thead>
|
||||
<q.tbody>
|
||||
{movies.map((movie: movieObject) => (
|
||||
<MovieTableRow
|
||||
movie={movie}
|
||||
key={movie.imdbID}
|
||||
onClick={() => handleModalOpen(movie)}
|
||||
/>
|
||||
))}
|
||||
</q.tbody>
|
||||
</q.table>
|
||||
</q.div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MovieTable
|
27
frontend/src/components/MovieTableRow/index.tsx
Normal file
27
frontend/src/components/MovieTableRow/index.tsx
Normal file
|
@ -0,0 +1,27 @@
|
|||
import type { movieObject } from '../../types/movie'
|
||||
import Modal from 'react-modal'
|
||||
import q from 'qjuul'
|
||||
import { useState } from 'react'
|
||||
|
||||
interface MovieTableRowProps {
|
||||
movie: movieObject
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
const mainAppElement = document.getElementById('root')
|
||||
|
||||
Modal.setAppElement(mainAppElement as HTMLElement)
|
||||
|
||||
const MovieTableRow: React.FC<MovieTableRowProps> = ({ movie, onClick }) => {
|
||||
return (
|
||||
<q.tr onClick={onClick} className="card rounded-md hover:cursor-pointer">
|
||||
<q.td className="p-2">
|
||||
<q.img src={movie.Poster} alt={movie.Title} width="100" />
|
||||
</q.td>
|
||||
<q.td>{movie.Title}</q.td>
|
||||
<q.td>{movie.Year}</q.td>
|
||||
</q.tr>
|
||||
)
|
||||
}
|
||||
|
||||
export default MovieTableRow
|
85
frontend/src/components/Pagination/index.tsx
Normal file
85
frontend/src/components/Pagination/index.tsx
Normal file
|
@ -0,0 +1,85 @@
|
|||
import q from 'qjuul'
|
||||
import PaginationButton from '../PaginationButton'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
interface PaginationProps {
|
||||
currentPage: number
|
||||
totalPages: number
|
||||
handlePageChange: (pageNumber: number) => void
|
||||
}
|
||||
|
||||
const Pagination: React.FC<PaginationProps> = ({
|
||||
currentPage,
|
||||
totalPages,
|
||||
handlePageChange,
|
||||
}) => {
|
||||
const [showLeftDots, setShowLeftDots] = useState(false)
|
||||
const [showRightDots, setShowRightDots] = useState(false)
|
||||
const [showingNumbers, setShowingNumbers] = useState([0])
|
||||
|
||||
useEffect(() => {
|
||||
if (totalPages > 5) {
|
||||
setShowLeftDots(currentPage > 4 ? true : false)
|
||||
setShowRightDots(currentPage < totalPages - 3 ? true : false)
|
||||
}
|
||||
if (!showLeftDots && !showRightDots) {
|
||||
setShowingNumbers(Array.from(Array(totalPages).keys()).map((n) => n + 1))
|
||||
}
|
||||
if (!showLeftDots && showRightDots) {
|
||||
setShowingNumbers([1, 2, 3, 4, 5])
|
||||
}
|
||||
|
||||
if (showLeftDots && !showRightDots) {
|
||||
setShowingNumbers([
|
||||
totalPages - 4,
|
||||
totalPages - 3,
|
||||
totalPages - 2,
|
||||
totalPages - 1,
|
||||
totalPages,
|
||||
])
|
||||
}
|
||||
|
||||
if (showLeftDots && showRightDots) {
|
||||
setShowingNumbers([currentPage - 1, currentPage, currentPage + 1])
|
||||
}
|
||||
}, [totalPages, currentPage, showLeftDots, showRightDots])
|
||||
|
||||
return (
|
||||
<q.div>
|
||||
{totalPages > 0 && (
|
||||
<q.div className="flex gap-4">
|
||||
{showLeftDots && (
|
||||
<q.div className="flex">
|
||||
<PaginationButton
|
||||
pageNumber={1}
|
||||
handlePageChange={handlePageChange}
|
||||
/>
|
||||
...
|
||||
</q.div>
|
||||
)}
|
||||
|
||||
{showingNumbers.map((page) => (
|
||||
<q.div key={page}>
|
||||
<PaginationButton
|
||||
pageNumber={page}
|
||||
handlePageChange={handlePageChange}
|
||||
isCurrentPage={page === currentPage}
|
||||
/>
|
||||
</q.div>
|
||||
))}
|
||||
{showRightDots && (
|
||||
<q.div className="flex">
|
||||
...
|
||||
<PaginationButton
|
||||
pageNumber={totalPages}
|
||||
handlePageChange={handlePageChange}
|
||||
/>{' '}
|
||||
</q.div>
|
||||
)}
|
||||
</q.div>
|
||||
)}
|
||||
</q.div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Pagination
|
0
frontend/src/components/Pagination/styles.css
Normal file
0
frontend/src/components/Pagination/styles.css
Normal file
28
frontend/src/components/PaginationButton/index.tsx
Normal file
28
frontend/src/components/PaginationButton/index.tsx
Normal file
|
@ -0,0 +1,28 @@
|
|||
import q from 'qjuul'
|
||||
|
||||
interface PaginationButtonProps {
|
||||
pageNumber: number
|
||||
isCurrentPage?: boolean
|
||||
handlePageChange: (pageNumber: number) => void
|
||||
}
|
||||
|
||||
const PaginationButton: React.FC<PaginationButtonProps> = ({
|
||||
pageNumber,
|
||||
handlePageChange,
|
||||
isCurrentPage,
|
||||
}) => {
|
||||
return (
|
||||
<q.div
|
||||
onClick={() => handlePageChange(pageNumber)}
|
||||
className={
|
||||
isCurrentPage
|
||||
? 'bg-red-600 p-2 rounded-md hover:cursor-pointer'
|
||||
: 'bg-red-400 p-2 rounded-md hover:cursor-pointer'
|
||||
}
|
||||
>
|
||||
<p>{pageNumber}</p>
|
||||
</q.div>
|
||||
)
|
||||
}
|
||||
|
||||
export default PaginationButton
|
0
frontend/src/components/PaginationButton/styles.css
Normal file
0
frontend/src/components/PaginationButton/styles.css
Normal file
5
frontend/src/components/index.ts
Normal file
5
frontend/src/components/index.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export { default as Pagination } from './Pagination'
|
||||
export { default as MovieTableRow } from './MovieTableRow'
|
||||
export { default as MovieForm } from './MovieForm'
|
||||
export { default as MovieTable } from './MovieTable'
|
||||
export { default as MovieModal } from './MovieModal'
|
Reference in a new issue