From f9c07264378d0d371419e1ab1a2e94b86ce8ad72 Mon Sep 17 00:00:00 2001 From: Papi Date: Mon, 11 Mar 2024 14:05:40 +0100 Subject: [PATCH] done --- package-lock.json | 113 ++++++++++++++++++++- package.json | 4 +- src/client/App.css | 6 ++ src/client/App.jsx | 15 +++ src/client/components/CreateMovie.jsx | 40 ++++++++ src/client/components/Home.jsx | 15 +++ src/client/components/LoginUser.jsx | 37 +++++++ src/client/components/Logout.jsx | 21 ++++ src/client/components/MovieForm.jsx | 6 +- src/client/components/MovieList.jsx | 23 +++++ src/client/components/MovieListContent.jsx | 14 +++ src/client/components/MovieListItems.jsx | 18 ++++ src/client/components/Movies.jsx | 36 +++++++ src/client/components/RegisterUser.jsx | 33 ++++++ src/client/components/UserForm.jsx | 1 + src/domain/movie.js | 22 ++++ src/domain/user.js | 16 +++ src/main.jsx | 7 +- src/server/controllers/movie.js | 34 ++++++- src/server/controllers/user.js | 29 ++++-- src/server/routers/movie.js | 4 +- src/styles/home.css | 22 ++++ src/styles/logout.css | 27 +++++ src/styles/movie-form.css | 36 +++++++ src/styles/movie-list-items.css | 33 ++++++ src/styles/movie-list.css | 31 ++++++ 26 files changed, 626 insertions(+), 17 deletions(-) create mode 100644 src/client/components/CreateMovie.jsx create mode 100644 src/client/components/Home.jsx create mode 100644 src/client/components/LoginUser.jsx create mode 100644 src/client/components/Logout.jsx create mode 100644 src/client/components/MovieList.jsx create mode 100644 src/client/components/MovieListContent.jsx create mode 100644 src/client/components/MovieListItems.jsx create mode 100644 src/client/components/Movies.jsx create mode 100644 src/client/components/RegisterUser.jsx create mode 100644 src/domain/movie.js create mode 100644 src/domain/user.js create mode 100644 src/styles/home.css create mode 100644 src/styles/logout.css create mode 100644 src/styles/movie-form.css create mode 100644 src/styles/movie-list-items.css create mode 100644 src/styles/movie-list.css diff --git a/package-lock.json b/package-lock.json index 143302ab..4503b851 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "dependencies": { "@prisma/client": "^5.7.1", + "axios": "^1.6.7", "bcrypt": "^5.1.1", "concurrently": "^7.6.0", "cors": "^2.8.5", @@ -16,7 +17,8 @@ "express": "^4.18.2", "jsonwebtoken": "^9.0.2", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^6.22.3" }, "devDependencies": { "@types/react": "^18.2.43", @@ -1057,6 +1059,14 @@ "@prisma/debug": "5.8.1" } }, + "node_modules/@remix-run/router": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", + "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.9.6", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.6.tgz", @@ -1567,6 +1577,11 @@ "has-symbols": "^1.0.3" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", @@ -1579,6 +1594,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", + "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "dependencies": { + "follow-redirects": "^1.15.4", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1847,6 +1872,17 @@ "color-support": "bin.js" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2098,6 +2134,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -2839,6 +2883,25 @@ "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -2848,6 +2911,19 @@ "is-callable": "^1.1.3" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -4491,6 +4567,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -4600,6 +4681,36 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.22.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz", + "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==", + "dependencies": { + "@remix-run/router": "1.15.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.22.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz", + "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==", + "dependencies": { + "@remix-run/router": "1.15.3", + "react-router": "6.22.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", diff --git a/package.json b/package.json index eb2dc20c..6c5eefbd 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ }, "dependencies": { "@prisma/client": "^5.7.1", + "axios": "^1.6.7", "bcrypt": "^5.1.1", "concurrently": "^7.6.0", "cors": "^2.8.5", @@ -18,7 +19,8 @@ "express": "^4.18.2", "jsonwebtoken": "^9.0.2", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-router-dom": "^6.22.3" }, "devDependencies": { "@types/react": "^18.2.43", diff --git a/src/client/App.css b/src/client/App.css index 635ac03f..d71b3a3b 100644 --- a/src/client/App.css +++ b/src/client/App.css @@ -36,7 +36,13 @@ transform: rotate(360deg); } } +.grid{ + display: grid; + +} ul { list-style: none; + margin: 0; + padding: 0; } \ No newline at end of file diff --git a/src/client/App.jsx b/src/client/App.jsx index bae3b635..f4ccea46 100644 --- a/src/client/App.jsx +++ b/src/client/App.jsx @@ -1,4 +1,7 @@ import { useEffect, useState } from 'react'; +import { Routes, Route } from 'react-router-dom'; +import Home from './components/Home'; +import MovieList from './components/MovieList'; import './App.css'; import MovieForm from './components/MovieForm'; import UserForm from './components/UserForm'; @@ -68,6 +71,18 @@ function App() { ); })} + + } + > + + } + > + + ); } diff --git a/src/client/components/CreateMovie.jsx b/src/client/components/CreateMovie.jsx new file mode 100644 index 00000000..f6c2fa1b --- /dev/null +++ b/src/client/components/CreateMovie.jsx @@ -0,0 +1,40 @@ +import axios from "axios" +import { useState } from "react" +import MovieForm from "./MovieForm" + +function CreateMovie({ apiUrl, movies, setMovies }) { + const [createMovieMessage, setCreateMovieMessage] = useState('') + + const handleCreateMovie = async ({ title, description, runtimeMins }) => { + const token = localStorage.getItem('token') + + try { + const { data } = await axios.post(`${apiUrl}/movie`, { title, + description, + runtimeMins + }, { + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + } + } + ) + const { movie, message } = data + setMovies([...movies, movie]) + setCreateMovieMessage(message) + } + catch (err) { + setCreateMovieMessage(err.response.data.error) + } + } + + return ( +
+

Create a movie

+ + {createMovieMessage &&

{createMovieMessage}

} +
+ ) +} + +export default CreateMovie \ No newline at end of file diff --git a/src/client/components/Home.jsx b/src/client/components/Home.jsx new file mode 100644 index 00000000..2a881802 --- /dev/null +++ b/src/client/components/Home.jsx @@ -0,0 +1,15 @@ +import RegisterUser from "./RegisterUser" +import LoginUser from "./LoginUser" +//import '../styles/home.css' + +function Home({ apiUrl }) { + + return ( +
+ + +
+ ) +} + +export default Home \ No newline at end of file diff --git a/src/client/components/LoginUser.jsx b/src/client/components/LoginUser.jsx new file mode 100644 index 00000000..5b8ef79d --- /dev/null +++ b/src/client/components/LoginUser.jsx @@ -0,0 +1,37 @@ +import axios from "axios" +import { useState } from "react" +import { useNavigate } from "react-router-dom" +import UserForm from "./UserForm" + +function LoginUser({ apiUrl }) { + const navigate = useNavigate() + const [loginMessage, setLoginMessage] = useState('') + + const handleLogin = async ({ username, password }) => { + try { + const { data } = await axios.post(`${apiUrl}/user/login`, { username, + password + }, { + headers: { 'Content-Type': 'application/json' }, + } + ) + const { token, message } = data + localStorage.setItem('token', token) + setLoginMessage(message) + navigate('/movie-list') + } + catch (err) { + setLoginMessage(err.response.data.error) + } + }; + + return ( +
+

Login

+ + {loginMessage &&

{loginMessage}

} +
+ ) + } + + export default LoginUser \ No newline at end of file diff --git a/src/client/components/Logout.jsx b/src/client/components/Logout.jsx new file mode 100644 index 00000000..6d7d3ccf --- /dev/null +++ b/src/client/components/Logout.jsx @@ -0,0 +1,21 @@ +import '../styles/logout.css' + +import { useNavigate } from "react-router-dom" + +function Logout() { + const navigate = useNavigate() + + const handleSubmit = () => { + const token = localStorage.getItem('token') + if (token) { + localStorage.clear() + navigate('/') + } + } + + return ( + + ) +} + +export default Logout \ No newline at end of file diff --git a/src/client/components/MovieForm.jsx b/src/client/components/MovieForm.jsx index a3d92402..45f3a8d2 100644 --- a/src/client/components/MovieForm.jsx +++ b/src/client/components/MovieForm.jsx @@ -1,4 +1,6 @@ import { useState } from "react"; +//import '../styles/movie-form.css' + export default function MovieForm({ handleSubmit }) { const [movie, setMovie] = useState({ title: '', description: '', runtimeMins: 60 }); @@ -6,6 +8,8 @@ export default function MovieForm({ handleSubmit }) { const handleSubmitDecorator = (e) => { e.preventDefault(); handleSubmit(movie); + setMovie({ title: '', description: '', runtimeMins: 60 }) + } const handleChange = (e) => { @@ -18,7 +22,7 @@ export default function MovieForm({ handleSubmit }) { } return ( -
+ diff --git a/src/client/components/MovieList.jsx b/src/client/components/MovieList.jsx new file mode 100644 index 00000000..0ae63849 --- /dev/null +++ b/src/client/components/MovieList.jsx @@ -0,0 +1,23 @@ +import MovieListContent from "./MovieListContent"; +//import '../styles/movie-list.css' + +function MovieList({ apiUrl }) { + const [movies, setMovies] = useState([]); + + useEffect(() => { + const getAllMovies = async () => { + const { data } = await axios.get(`${apiUrl}/movie`) + setMovies(data.data) + } + getAllMovies() + }, [apiUrl]); + + return ( +
+ + +
+ ) +} + +export default MovieList \ No newline at end of file diff --git a/src/client/components/MovieListContent.jsx b/src/client/components/MovieListContent.jsx new file mode 100644 index 00000000..713e1d3a --- /dev/null +++ b/src/client/components/MovieListContent.jsx @@ -0,0 +1,14 @@ +import CreateMovie from "./CreateMovie"; +import Movies from "./Movies"; + +function MovieListContent({ apiUrl, movies, setMovies }) { + + return ( +
+ + +
+ ) +} + +export default MovieListContent \ No newline at end of file diff --git a/src/client/components/MovieListItems.jsx b/src/client/components/MovieListItems.jsx new file mode 100644 index 00000000..2772071a --- /dev/null +++ b/src/client/components/MovieListItems.jsx @@ -0,0 +1,18 @@ +//import '../styles/movie-list-items.css' + +function MovieListItems({ movies }) { + + return ( + + ) +} + +export default MovieListItems \ No newline at end of file diff --git a/src/client/components/Movies.jsx b/src/client/components/Movies.jsx new file mode 100644 index 00000000..de5ce578 --- /dev/null +++ b/src/client/components/Movies.jsx @@ -0,0 +1,36 @@ +import axios from "axios" +import { useState } from "react" +import MovieListItems from "./MovieListItems" + +function Movies({ apiUrl, movies, setMovies }) { + const [deleteAllMoviesMessage, setDeleteAllMoviesMessage] = useState('') + + const deleteAllMovies = async () => { + const token = localStorage.getItem('token') + + try { + const { data } = await axios.delete(`${apiUrl}/movie`, { + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + } + }) + setMovies([]) + setDeleteAllMoviesMessage(data.message) + } + catch (err) { + setDeleteAllMoviesMessage(err.response.data.eror) + } + } + + return ( +
+

Movie list

+ + {deleteAllMoviesMessage &&

{deleteAllMoviesMessage}

} + +
+ ) +} + +export default Movies \ No newline at end of file diff --git a/src/client/components/RegisterUser.jsx b/src/client/components/RegisterUser.jsx new file mode 100644 index 00000000..941bc232 --- /dev/null +++ b/src/client/components/RegisterUser.jsx @@ -0,0 +1,33 @@ +import axios from "axios"; +import { useState } from "react"; +import UserForm from "./UserForm" + +function RegisterUser({ apiUrl }) { + const [registerMessage, setRegisterMessage] = useState('') + + const handleRegister = async ({ username, password }) => { + try { + const { data } = await axios.post(`${apiUrl}/user/register`, { + username, + password + }, { + headers: { 'Content-Type': 'application/json' }, + } + ) + setRegisterMessage(data.message) + } + catch (err) { + setRegisterMessage(err.response.data.error) + } + }; + + return ( +
+

Register

+ + {registerMessage &&

{registerMessage}

} +
+ ) +} + +export default RegisterUser \ No newline at end of file diff --git a/src/client/components/UserForm.jsx b/src/client/components/UserForm.jsx index 328f90b4..416a5c5b 100644 --- a/src/client/components/UserForm.jsx +++ b/src/client/components/UserForm.jsx @@ -6,6 +6,7 @@ export default function UserForm({ handleSubmit }) { const handleSubmitDecorator = (e) => { e.preventDefault(); handleSubmit(user); + setUser({ username: '', password: '' }) }; const handleChange = (e) => { diff --git a/src/domain/movie.js b/src/domain/movie.js new file mode 100644 index 00000000..947ac86c --- /dev/null +++ b/src/domain/movie.js @@ -0,0 +1,22 @@ +import { PrismaClient } from '@prisma/client' +const prisma = new PrismaClient(); + +const getAllMoviesDb = async () => await prisma.movie.findMany() + +const checkTitleExistsDb = async (title) => await prisma.movie.findUnique({ + where: { + title + } +}) + +const createMovieDb = async (title, description, runtimeMins) => await prisma.movie.create({ + data: { + title, + description, + runtimeMins + } +}) + +const deleteAllMoviesDb = async () => await prisma.movie.deleteMany({}) + +export { getAllMoviesDb, createMovieDb, checkTitleExistsDb, deleteAllMoviesDb } \ No newline at end of file diff --git a/src/domain/user.js b/src/domain/user.js new file mode 100644 index 00000000..a3cb2167 --- /dev/null +++ b/src/domain/user.js @@ -0,0 +1,16 @@ + +import { PrismaClient } from '@prisma/client' +const prisma = new PrismaClient(); + +const findUserDb = async (username) => await prisma.user.findUnique({ + where: { username } +}) + +const createUserDb = async (username, password) => await prisma.user.create({ + data: { + username, + password + } +}) + +export { findUserDb, createUserDb } \ No newline at end of file diff --git a/src/main.jsx b/src/main.jsx index 7749a5cf..5b3f77d0 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -1,10 +1,13 @@ import React from 'react' import ReactDOM from 'react-dom/client' +import { BrowserRouter } from 'react-router-dom' import App from './client/App.jsx' import './index.css' ReactDOM.createRoot(document.getElementById('root')).render( - + + + , -) +); diff --git a/src/server/controllers/movie.js b/src/server/controllers/movie.js index d4733b61..b8bce8d7 100644 --- a/src/server/controllers/movie.js +++ b/src/server/controllers/movie.js @@ -3,29 +3,55 @@ import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient(); const jwtSecret = 'mysecret'; +import { getAllMoviesDb, createMovieDb, checkTitleExistsDb, deleteAllMoviesDb } from '../domains/movie.js'; +const secret = process.env.JWT_SECRET const getAllMovies = async (req, res) => { - const movies = await prisma.movie.findMany(); - + const movies = await prisma.movie.getAllMoviesDb(); res.json({ data: movies }); }; const createMovie = async (req, res) => { const { title, description, runtimeMins } = req.body; + const titleExists = await checkTitleExistsDb(title) + if (titleExists) return res.status(409).json({ error: `${title} is already in the movie list`}) try { const token = null; // todo verify the token } catch (e) { return res.status(401).json({ error: 'Invalid token provided.' }) + const token = req.headers.authorization.slice(7) + jwt.verify(token, secret) + } catch { + return res.status(401).json({ error: 'Invalid token provided, you must be signed in to delete movies.' }) } - const createdMovie = null; + //const createdMovie = null; res.json({ data: createdMovie }); + const createdMovie = await createMovieDb(title, description, runtimeMins) + res.json({ + movie: createdMovie, + message: `${title} has successfully been added to the movie list!` + }); }; +const deleteAllMovies = async (req, res) => { + try { + const token = req.headers.authorization.slice(7) + jwt.verify(token, secret) + } catch { + return res.status(401).json({ error: 'Invalid token provided, you must be signed in to delete movies.' }) + } + + await deleteAllMoviesDb() + return res.status(200).json({ message: 'All movie list movies have been deleted :(' }) +} export { getAllMovies, - createMovie + createMovie, + getAllMovies, + createMovie, + deleteAllMovies }; diff --git a/src/server/controllers/user.js b/src/server/controllers/user.js index 05db4183..68d9a89d 100644 --- a/src/server/controllers/user.js +++ b/src/server/controllers/user.js @@ -4,33 +4,48 @@ import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient(); const jwtSecret = 'mysecret'; +import { findUserDb, createUserDb } from '../domains/user.js'; +const secret = process.env.JWT_SECRET + const register = async (req, res) => { const { username, password } = req.body; const createdUser = null; + const isUsernameUnique = await findUserDb(username) + if (isUsernameUnique) return res.status(409).json({ error: `Username ${username} has already been taken, please try a different username`}) res.json({ data: createdUser }); + const hashedPassword = await bcrypt.hash(password, 12) + try { + const createdUser = await createUserDb(username, hashedPassword) + return res.status(201).json({ + data: createdUser, + message: `Thank you, ${username}, your registration is now complete.` + }) + } + catch (err) { + return res.status(500).json({ error: 'Server error, please try again'}) + } }; const login = async (req, res) => { const { username, password } = req.body; - const foundUser = null; + const foundUser = await findUserDb(username) if (!foundUser) { return res.status(401).json({ error: 'Invalid username or password.' }); } - - const passwordsMatch = false; + const passwordsMatch = await bcrypt.compare(password, foundUser.password) if (!passwordsMatch) { return res.status(401).json({ error: 'Invalid username or password.' }); } - - const token = null; - - res.json({ data: token }); + + const token = jwt.sign({ username }, secret) + res.json({ token, message: `You have successfully logged in as ${username}` + }); }; export { diff --git a/src/server/routers/movie.js b/src/server/routers/movie.js index a606c4c9..59769558 100644 --- a/src/server/routers/movie.js +++ b/src/server/routers/movie.js @@ -1,9 +1,11 @@ import express from 'express'; -import { getAllMovies, createMovie } from '../controllers/movie.js'; +import { getAllMovies, createMovie, deleteAllMovies} from '../controllers/movie.js'; const router = express.Router(); router.get('/', getAllMovies); router.post('/', createMovie); +router.delete('/', deleteAllMovies) + export default router; diff --git a/src/styles/home.css b/src/styles/home.css new file mode 100644 index 00000000..33409ae7 --- /dev/null +++ b/src/styles/home.css @@ -0,0 +1,22 @@ + +.home--container{ + grid-template-rows: repeat(2, 1fr); + gap: 100px; + justify-content: center; + padding: 100px; +} + +.home--form-container { + grid-template-rows: repeat(2, auto) 60px; + gap: 10px; + border-radius: 8px; + padding: 20px 50px; +} + +.home--register-container { + background: linear-gradient(325deg, #021c7a 22.81%, #2453ff 99.29%); +} + +.home--login-container { + background: linear-gradient(135deg, #021c7a 19.25%, #2453ff 100.00%); +} \ No newline at end of file diff --git a/src/styles/logout.css b/src/styles/logout.css new file mode 100644 index 00000000..9fa4c7d0 --- /dev/null +++ b/src/styles/logout.css @@ -0,0 +1,27 @@ +.logout-btn { + justify-self: end; + align-self: center; + width: 150px; + height: 50px; + border: none; + color: #00ffff; + font-weight: bold; + font-size: 20px; + border-radius: 16px; + background-color: #000000; + box-shadow: 1px 1px 0 #00ffff, 0 0 5px 0 #00ffff, 0 0 10px 0 #00ffff; + transition: .8s; +} + +.logout-btn:hover { + transform: scale(1.05); + box-shadow: 1px 1px 0 #ff0000, 0 0 5px 2px #ff0000, 0 0 5px 0 #ff0000; + color: #ff0000; + cursor: pointer; +} + +.logout-message { + position: fixed; + right: 260px; + top: 100px; +} \ No newline at end of file diff --git a/src/styles/movie-form.css b/src/styles/movie-form.css new file mode 100644 index 00000000..8288f72e --- /dev/null +++ b/src/styles/movie-form.css @@ -0,0 +1,36 @@ +.movie-form input { + width: 100%; + padding: 12px 20px; + margin: 8px 0; + display: inline-block; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; + border: none; + outline: none; +} + +.movie-form input:focus::placeholder { + color: black; +} + +.movie-form input:focus { + background-color: #00d0fae2; +} + +.movie-form button { + justify-self: end; + background-color: #040dbe; + color: white; + margin-top: 10px; + font-size: 16px; + padding: 10px 20px; + border: none; + border-radius: 6px; + cursor: pointer; +} + +.movie-form button:hover { + background-color: #000dff; + transform: scale(1.05); +} \ No newline at end of file diff --git a/src/styles/movie-list-items.css b/src/styles/movie-list-items.css new file mode 100644 index 00000000..2d3c1ad9 --- /dev/null +++ b/src/styles/movie-list-items.css @@ -0,0 +1,33 @@ +.movie-list-items--container { + grid-template-columns: repeat(3, minmax(200px, auto)); + grid-auto-flow: row; + gap: 20px; + padding: 20px 40px; +} + +.movie-list-items--item > h3, p { + margin: 0; +} + +.movie-list-items--item { + grid-template-rows: 40px repeat(2, 20px); + gap: 5px; + align-items: center; + background-color: #197023; + border-radius: 8px; + box-shadow: 1px 1px 5px #000000, 0 0 0 1px #1b1515; + padding: 8px; + transition: .3s; +} + +.movie-list-items--item:hover { + transform: scale(1.05); + cursor: default; +} + +.movie-list-items--item > h3 { + text-decoration: underline; + text-underline-offset: 2px; + font-weight: 500; + color: #310b2d; +} \ No newline at end of file diff --git a/src/styles/movie-list.css b/src/styles/movie-list.css new file mode 100644 index 00000000..4b649aa7 --- /dev/null +++ b/src/styles/movie-list.css @@ -0,0 +1,31 @@ +.movie-list--container { + grid-template-rows: 70px 1fr; + gap: 20px; + padding: 50px; +} + +.movie-list--content-container { + justify-content: center; +} + +.movie-list--movies-container { + justify-items: center; + gap: 20px; +} + +.delete-movies-btn { + width: 200px; + height: 40px; + background-color: #2600ff; + color: #ffffff; + font-size: 16px; + border: none; + border-radius: 9999px; +} + +.delete-movies-btn:hover { + transform: scale(1.02); + background-color: #2600ff9f; + cursor: pointer; + border: none; +} \ No newline at end of file