🧱 added backend

Co-authored-by: haraldnilsen <harald_998@hotmail.com>
This commit is contained in:
Sindre Kjelsrud 2023-09-13 09:37:55 +02:00
parent cd2dbd6eee
commit a260cb63b7
29 changed files with 16 additions and 4 deletions

View 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

View 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

View 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

View 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

View 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

View 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

View 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'