Commit 308d4f7f authored by Jiwon Yoon's avatar Jiwon Yoon
Browse files

Merge branch 'kimpen'

parents fa2af12b 1241f3e0
...@@ -6,28 +6,37 @@ import styles from "./movie-table.module.scss"; ...@@ -6,28 +6,37 @@ import styles from "./movie-table.module.scss";
const MovieTable = ({ movieList }) => { const MovieTable = ({ movieList }) => {
const [error, setError] = useState("") const [error, setError] = useState("")
async function handleClick(e, movieId) { async function handleSubmit(e, movieId) {
e.preventDefault(); e.preventDefault();
try { try {
setError(""); setError("")
await movieApi.submit(movieId) await movieApi.submit(movieId)
alert("서버 등록이 완료되었습니다.") alert("서버 등록이 완료되었습니다.")
window.location.reload()
} catch (error) {
catchErrors(error, setError)
}
}
async function handleDelete(e, movieId) {
e.preventDefault()
try {
setError("")
await movieApi.remove(movieId)
alert("해당 영화 정보가 서버에서 삭제되었습니다.")
window.location.reload()
} catch (error) { } catch (error) {
catchErrors(error, setError) catchErrors(error, setError)
} }
} }
return ( return (
<table className={`table text-center ${styles.tableForm}`}> <table className={`table text-center align-middle ${styles.tableForm}`}>
<thead className={`table-dark ${styles.dNone}`}> <thead className={`table-dark ${styles.dNone}`}>
<tr> <tr>
<th>제목</th> <th className="col-md-5">제목</th>
<th>감독</th> <th className="col-md-4">감독</th>
<th>상영일</th> <th className="col-md-3">상영일</th>
<th>줄거리</th>
<th>포스터</th>
<th>스틸컷</th>
<th>예고편</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
...@@ -35,24 +44,15 @@ const MovieTable = ({ movieList }) => { ...@@ -35,24 +44,15 @@ const MovieTable = ({ movieList }) => {
<> <>
<tr className={styles.Row} data-bs-toggle="collapse" data-bs-target={"#movie" + movie.id}> <tr className={styles.Row} data-bs-toggle="collapse" data-bs-target={"#movie" + movie.id}>
<td className="d-inline-block d-md-table-cell">{movie.title}</td> <td className="d-inline-block d-md-table-cell">{movie.title}</td>
<td data-label="- " className={`d-inline-block d-md-table-cell ${styles.data}`}>케이트 쇼트랜드</td> <td className="d-none d-md-table-cell">{movie.director}</td>
<td data-label="/ " className={`d-inline-block d-md-table-cell ${styles.data}`}>{movie.release_date}</td> <td className="d-none d-md-table-cell">{movie.release_date}</td>
<td className="d-none d-md-table-cell">{movie.overview !== '' ? 'O' : 'X'}</td>
<td className="d-none d-md-table-cell">{movie.poster_path !== '' ? 'O' : 'X'}</td>
<td className="d-none d-md-table-cell">{movie.backdrop_path !== '' ? 'O' : 'X'}</td>
<td className="d-none d-md-table-cell">{movie.video !== false ? 'O' : 'X'}</td>
</tr> </tr>
<tr className={styles.Row}> <tr className={styles.Row}>
<td colSpan="7" className="collapse" id={"movie" + movie.id}> <td colSpan="3" className="collapse" id={"movie" + movie.id}>
<div className={`d-inline-block d-md-none ${styles.word} mb-2`}> <div className={`d-inline-block d-md-none ${styles.word} mb-2`}>{movie.director} / {movie.release_date}</div>
줄거리 - {movie.overview !== '' ? 'O' : 'X'} /
포스터 - {movie.poster_path !== '' ? 'O' : 'X'} /
스틸컷 - {movie.backdrop_path !== '' ? 'O' : 'X'} /
예고편 - {movie.video !== false ? 'O' : 'X'}
</div>
<div className="d-flex justify-content-end"> <div className="d-flex justify-content-end">
<button type="button" className="btn btn-primary" onClick={(e) => handleClick(e, movie.id)}>등록</button> {movie.existed ? <button type="button" className="btn btn-danger" onClick={(e) => handleDelete(e, movie.id)}>삭제</button>
{/* <button type="button" className="btn btn-danger">삭제</button> */} : <button type="button" className="btn btn-primary" onClick={(e) => handleSubmit(e, movie.id)}>등록</button>}
</div> </div>
</td> </td>
</tr> </tr>
......
.Row:hover { .Row {
cursor: pointer;
&:hover {
background: rgba(0, 0, 0, 0.075); background: rgba(0, 0, 0, 0.075);
}
} }
.word { .word {
...@@ -17,11 +20,6 @@ ...@@ -17,11 +20,6 @@
& .Row { & .Row {
border-bottom: 2px solid; border-bottom: 2px solid;
& .data::before {
content: attr(data-label);
font-weight: bold;
}
} }
} }
}; };
\ No newline at end of file
...@@ -3,7 +3,7 @@ import { useHistory } from "react-router"; ...@@ -3,7 +3,7 @@ import { useHistory } from "react-router";
import Search from "../Search"; import Search from "../Search";
const MainNav = () => { const MainNav = () => {
const [search, setSearch] = useState({ keyword: "" }) const [search, setSearch] = useState({ type: "home", keyword: "" })
const history = useHistory() const history = useHistory()
function searchMovie() { function searchMovie() {
...@@ -15,7 +15,7 @@ const MainNav = () => { ...@@ -15,7 +15,7 @@ const MainNav = () => {
<a className="nav-link text-white" href="/movielist">영화</a> <a className="nav-link text-white" href="/movielist">영화</a>
<a className="nav-link text-white" href="/ticket">빠른예매</a> <a className="nav-link text-white" href="/ticket">빠른예매</a>
<a className="nav-link text-white" href="/theater">극장</a> <a className="nav-link text-white" href="/theater">극장</a>
<Search type="home" search={search} setSearch={setSearch} handleClick={searchMovie} /> <Search search={search} setSearch={setSearch} handleClick={searchMovie} />
</nav> </nav>
) )
} }
......
import styles from "./search.module.scss"; import styles from "./search.module.scss";
const Search = ({ type, search, setSearch, handleClick }) => { const Search = ({ search, setSearch, handleClick }) => {
function handleSearch(e) { function handleSearch(e) {
const { name, value } = e.target const { name, value } = e.target
setSearch({ ...search, [name]: value }) setSearch({ ...search, [name]: value })
} }
console.log("type==", type)
return ( return (
<div className="d-flex"> <div className="d-flex">
{type === "home" ? null : <input className={`form-control ${search.type === "home" ? styles.searchWhite : `${styles.search}`}`} name="keyword" type="text" autoComplete="off" onChange={handleSearch} />
<select className={`form-select ${styles.search}`} name="kind" aria-label="select search" onChange={handleSearch}> <i className={`bi bi-search align-self-center ${search.type === "home" ? "text-white" : "mx-2"} ${styles.icon}`} onClick={handleClick} style={{ fontSize: "1.3rem" }}></i>
<option selected value="title">제목</option>
<option value="director">감독명</option>
</select>
}
<input className={`form-control ${type === "home" ? styles.searchWhite : `${styles.search}`}`} name="keyword" type="text" onChange={handleSearch} />
<i className={`bi bi-search align-self-center ${type === "home" ? "text-white" : "mx-2"} ${styles.icon}`} onClick={handleClick} style={{ fontSize: "1.3rem" }}></i>
</div> </div>
) )
} }
......
import { useLocation } from 'react-router-dom'; import { useState, useEffect } from 'react'
import queryString from 'query-string'; import { useLocation } from 'react-router-dom'
import MovieChart from "./MovieChart/index.js"; import queryString from 'query-string'
import MovieCard from "./MovieCard/index.js"
import movieApi from '../apis/movie.api.js'
import catchErrors from '../utils/catchErrors.js'
const SearchResult = () => { const SearchResult = () => {
const [result, setResult] = useState([])
const [error, setError] = useState("")
const { search } = useLocation() const { search } = useLocation()
const { title } = queryString.parse(search) const { title } = queryString.parse(search)
console.log("search==",search,"title==",title) useEffect(() => {
findforKeyword()
}, [title])
async function findforKeyword() {
try {
setError("")
const { count, results } = await movieApi.search({ type: "home", keyword: title })
setResult([...results])
} catch (error) {
catchErrors(error, setError)
}
}
return ( return (
<>
{result.length !== 0 ? (
<> <>
<h3 className="text-white text-center my-5">'{title}' 관한 검색 결과입니다.</h3> <h3 className="text-white text-center my-5">'{title}' 관한 검색 결과입니다.</h3>
<MovieChart /> <div className="row row-cols-1 row-cols-md-4 g-4">
<MovieCard list={result} />
</div>
</>
) : <h3 className="text-white text-center my-5 vh-100" style={{ wordBreak: "keep-all" }}>'{title}' 관한 검색 결과가 존재하지 않습니다.</h3>
}
</> </>
) )
} }
......
import { useState } from 'react' import { useState } from 'react'
import MovieChart from '../components/MovieChart/MovieChart' import MovieChart from '../components/MovieChart.js'
import MovieComming from '../components/MovieComming/MovieComming' import MovieComming from '../components/MovieComming.js'
const MovieListPage = () => { const MovieListPage = () => {
const [state, setState] = useState(true) const [state, setState] = useState(true)
return ( return (
<div className="container"> <div className="container">
<div className=""> <div>
<ul className="nav nav-tabs justify-content-center my-4 border-0" id="myTab" role="tablist"> <ul className="nav nav-tabs justify-content-center my-4 border-0" id="myTab" role="tablist">
<li className="nav-item" role="presentation"> <li className="nav-item" role="presentation">
<button className="nav-link active mx-auto" style={{color:"white", borderColor: "black", backgroundColor:"black", borderBottom: state? "3px solid":"none" ,borderBottomColor:state?"#FEDC00":"black"}} id="moviechart-tab" data-bs-toggle="tab" data-bs-target="#moviechart" type="button" role="tab" aria-controls="moviechart" aria-selected="true" onClick={() => setState(true)}>무비차트</button> <button className="nav-link active mx-auto" style={{color:"white", borderColor: "black", backgroundColor:"black", borderBottom: state? "3px solid":"none" ,borderBottomColor:state?"#FEDC00":"black"}} id="moviechart-tab" data-bs-toggle="tab" data-bs-target="#moviechart" type="button" role="tab" aria-controls="moviechart" aria-selected="true" onClick={() => setState(true)}>무비차트</button>
......
import { Cinema } from "../db/index.js";
const getAll = async (req, res) => {
try {
const info = await Cinema.findOne({
where: { id: 1 },
attributes: ['cinemaName', 'transportation', 'parking', 'address']
})
return res.json(info)
} catch (error) {
return res.status(500).send(error.message || "영화관 정보 가져오는 중 에러 발생")
}
}
const edit = async (req, res) => {
try {
let response = null
const result = await Cinema.findOrCreate({
where: { id: 1 },
defaults: { ...req.body }
})
if (!result[1]) {
const updateData = await Cinema.update({ ...req.body }, { where: { id: 1 } })
response = updateData
} else response = result[0]
return res.json(response)
} catch (error) {
return res.status(500).send(error.message || "영화관 정보 수정 중 에러 발생")
}
}
export default {
getAll,
edit
}
\ No newline at end of file
...@@ -45,7 +45,103 @@ const getMovieById = async (req, res) => { ...@@ -45,7 +45,103 @@ const getMovieById = async (req, res) => {
console.log(elements) console.log(elements)
res.json(elements) res.json(elements)
} catch (error) { } catch (error) {
return res.status(500).send(error.message || "영화 가져오기 중 에러 발생");
}
}
const getMovieByCategory = async (req, res, next, category) => {
try {
const TMDBmovieIds = []
const movieIds = []
const response = await axios.get(`https://api.themoviedb.org/3/movie/${category}?api_key=${process.env.TMDB_APP_KEY}&language=ko-KR&page=1`)
const TMDBmovies = response.data.results
TMDBmovies.forEach(element => {
TMDBmovieIds.push(element.id)
})
const responseAfterCompare = await Movie.findAll({
where: {
movieId: {
[Op.or]: TMDBmovieIds
}
}
})
responseAfterCompare.forEach(el => {
movieIds.push(el.movieId)
})
console.log('movieIds=', movieIds)
req.movieIds = movieIds
next()
} catch (error) {
return res.status(500).send(error.message || "영화 가져오기 중 에러 발생");
}
}
const getMovieById = async (req, res) => {
try {
const movieIds = req.movieIds
console.log(movieIds)
const elements = await Promise.all(
movieIds.map(async (movieId) => {
const movie = await axios.get(`https://api.themoviedb.org/3/movie/${movieId}?api_key=${process.env.TMDB_APP_KEY}&language=ko-KR`)
return movie.data
})
)
console.log(elements)
res.json(elements)
} catch (error) {
return res.status(500).send(error.message || "영화 가져오기 중 에러 발생");
}
}
const movieforAdmin = async (req, res) => {
try {
const TMDBmovieIds = []
const TMDBmovies = req.TMDBmovies
TMDBmovies.forEach(element => {
TMDBmovieIds.push(element.id)
})
const findDirectorResult = await Promise.all(TMDBmovieIds.map(async (movieId) => {
let newObj = { id: movieId, name: "" }
const { data } = await axios.get(`https://api.themoviedb.org/3/movie/${movieId}/credits?api_key=${process.env.TMDB_APP_KEY}&language=ko-KR`)
const findDirectors = await data.crew.filter(element => element.job === "Director")
if (findDirectors.length !== 0) {
const name = findDirectors.reduce((acc, cur, idx) => {
if (idx !== 0) return acc + ', ' + cur.name
else return acc + cur.name}, '')
newObj.name = name
} else newObj.name = "없음"
return newObj
}))
findDirectorResult.forEach(element => TMDBmovies.forEach(movie => {
if (element.id === movie.id) movie.director = element.name
}))
const responseAfterCompare = await Movie.findAll({
where: {
movieId: TMDBmovieIds
},
attributes: ['movieId']
})
responseAfterCompare.forEach(element => TMDBmovies.forEach((movie) => {
if (movie.existed !== true && movie.id === element.movieId) movie.existed = true
else if (movie.existed !== true) movie.existed = false
}))
return res.json(TMDBmovies)
} catch (error) {
return res.status(500).send(error.message || "영화 가져오는 중 에러 발생")
}
}
const getAllMovie = async (req, res, next) => {
try {
const { pageNum } = req.query
const now = new Date()
const monthAgo = new Date(now.setMonth(now.getMonth() - 1)).toJSON().split(/T/)[0]
const response = await axios.get(`https://api.themoviedb.org/3/discover/movie?api_key=${process.env.TMDB_APP_KEY}&language=ko-KR&region=KR&sort_by=release_date.asc&release_date.gte=${monthAgo}&page=${pageNum}`)
req.TMDBmovies = response.data.results
next()
} catch (error) {
return res.status(500).send(error.message || "영화 가져오는 중 에러 발생")
} }
} }
...@@ -73,16 +169,69 @@ const getMovieList = async(req,res)=>{ ...@@ -73,16 +169,69 @@ const getMovieList = async(req,res)=>{
const create = async (req, res) => { const create = async (req, res) => {
try { try {
const { movieId } = req.params const { movieId } = req.params
const newMovie = await Movie.create({ movieId: movieId }); const { data } = await axios.get(`https://api.themoviedb.org/3/movie/${movieId}?api_key=${process.env.TMDB_APP_KEY}&language=ko-KR`)
res.json(newMovie); const newMovie = await Movie.create({ movieId: data.id, title: data.title, release_date: data.release_date })
return res.json(newMovie)
} catch (error) {
return res.status(500).send(error.message || "영화 등록 중 에러 발생")
}
}
const remove = async (req, res) => {
try {
const { movieId } = req.params
const delMovie = await Movie.destroy({ where: { movieId: movieId } })
return res.json(delMovie)
} catch (error) {
return res.status(500).send(error.message || "영화 삭제 중 에러 발생");
}
}
const findonlyTitle = async (req, res) => {
try {
const { keyword } = req.query
const movieIds = []
const { count, rows } = await Movie.findAndCountAll({
where: {
title: {
[Op.substring]: keyword
}
}
});
if (rows) {
rows.forEach(movie => movieIds.push(movie.movieId))
const elements = await Promise.all(
movieIds.map(async (movieId) => {
const movie = await axios.get(`https://api.themoviedb.org/3/movie/${movieId}?api_key=${process.env.TMDB_APP_KEY}&language=ko-KR`)
return movie.data
})
)
return res.json({ count: movieIds.length, results: elements })
} else return res.json({ count: count, results: rows })
} catch (error) {
return res.status(500).send(error.message || "영화 검색 중 에러 발생");
}
}
const findaboutAll = async (req, res, next) => {
try {
const { keyword } = req.query
const response = await axios.get(`https://api.themoviedb.org/3/search/movie?api_key=${process.env.TMDB_APP_KEY}&language=kr-KR&query=${encodeURI(keyword)}&region=KR`)
req.TMDBmovies = response.data.results
next()
} catch (error) { } catch (error) {
res.status(500).send(error.message || "영화 등록 중 에러 발생"); return res.status(500).send(error.message || "영화 검색 중 에러 발생");
} }
} }
export default { export default {
getMovieByCategory, getMovieByCategory,
getMovieById, getMovieById,
getAllMovie,
getMovieList, getMovieList,
create, create,
remove,
findonlyTitle,
findaboutAll,
movieforAdmin
} }
import { Sequelize } from "sequelize"; import { Sequelize } from "sequelize";
import UserModel from "../models/user.model.js"; import UserModel from "../models/user.model.js";
import RoleModel from "../models/role.model.js";
import MovieModel from "../models/movie.model.js"; import MovieModel from "../models/movie.model.js";
import CinemaModel from "../models/cinema.model.js";
import dbConfig from "../config/db.config.js"; import dbConfig from "../config/db.config.js";
const sequelize = new Sequelize( const sequelize = new Sequelize(
...@@ -20,10 +22,17 @@ const sequelize = new Sequelize( ...@@ -20,10 +22,17 @@ const sequelize = new Sequelize(
); );
const User = UserModel(sequelize) const User = UserModel(sequelize)
const Role = RoleModel(sequelize)
const Movie = MovieModel(sequelize) const Movie = MovieModel(sequelize)
const Cinema = CinemaModel(sequelize)
User.belongsTo(Role);
Role.hasOne(User);
export { export {
sequelize, sequelize,
User, User,
Movie Role,
Movie,
Cinema
} }
\ No newline at end of file
import Sequelize from "sequelize";
const { DataTypes } = Sequelize;
const CinemaModel = (sequelize) => {
const Cinema = sequelize.define(
"cinema",
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
cinemaName: {
type: DataTypes.STRING,
},
transportation: {
type: DataTypes.TEXT
},
parking: {
type: DataTypes.TEXT
},
address: {
type: DataTypes.STRING
}
},
{
timestamps: true,
freezeTableName: true,
tableName: "cinemas"
}
);
return Cinema;
};
export default CinemaModel;
\ No newline at end of file
...@@ -15,6 +15,12 @@ const MovieModel = (sequelize) => { ...@@ -15,6 +15,12 @@ const MovieModel = (sequelize) => {
type: DataTypes.INTEGER, type: DataTypes.INTEGER,
unique: true, unique: true,
}, },
title: {
type: DataTypes.STRING,
},
release_date: {
type: DataTypes.STRING,
},
ticket_sales: { ticket_sales: {
type: DataTypes.FLOAT, type: DataTypes.FLOAT,
defaultValue: 0.0, defaultValue: 0.0,
......
...@@ -2,12 +2,12 @@ import Sequelize from "sequelize"; ...@@ -2,12 +2,12 @@ import Sequelize from "sequelize";
const { DataTypes } = Sequelize; const { DataTypes } = Sequelize;
// export const ROLE_NAME = { export const ROLE_NAME = {
// USER = "user", USER : "user",
// MEMBER = "member", MEMBER : "member",
// ADMIN = "admin", ADMIN : "admin",
// ROOT = "root", ROOT : "root",
// } }
const RoleModel = (sequelize) => { const RoleModel = (sequelize) => {
const Role = sequelize.define( const Role = sequelize.define(
......
...@@ -28,11 +28,6 @@ const UserModel = (sequelize) => { ...@@ -28,11 +28,6 @@ const UserModel = (sequelize) => {
phoneNumber: { phoneNumber: {
type: DataTypes.INTEGER type: DataTypes.INTEGER
}, },
// role: {
// type: DataTypes.ENUM({
// values: Object.values(ROLE_NAME),
// }),
// }
}, },
{ {
timestamps: true, timestamps: true,
......
import express from "express";
import cinemaCtrl from "../controllers/cinema.controller.js";
const router = express.Router();
router
.route("/")
.get(cinemaCtrl.getAll)
.put(cinemaCtrl.edit)
export default router;
\ No newline at end of file
import express from "express"; import express from "express";
import userRouter from './user.route.js' import userRouter from './user.route.js'
import movieRouter from './movie.route.js' import movieRouter from './movie.route.js'
import cinemaRouter from "./cinema.route.js";
const router = express.Router(); const router = express.Router();
router.use('/movie', movieRouter) router.use('/movie', movieRouter)
router.use('/auth', userRouter) router.use('/auth', userRouter)
router.use('/cinema', cinemaRouter)
export default router; export default router;
\ No newline at end of file
...@@ -13,9 +13,26 @@ router.route('/showmovies/:category') ...@@ -13,9 +13,26 @@ router.route('/showmovies/:category')
router.route('/movielist') router.route('/movielist')
.get(movieCtrl.getMovieList) .get(movieCtrl.getMovieList)
router
.route("/all")
.get(movieCtrl.getAllMovie,
movieCtrl.movieforAdmin
)
router
.route("/search/home")
.get(movieCtrl.findonlyTitle)
router
.route("/search/admin")
.get(movieCtrl.findaboutAll,
movieCtrl.movieforAdmin
)
router router
.route("/:movieId") .route("/:movieId")
.post(movieCtrl.create) .post(movieCtrl.create)
.delete(movieCtrl.remove)
router.param('category', movieCtrl.getMovieByCategory) router.param('category', movieCtrl.getMovieByCategory)
export default router; export default router;
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment