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";
const MovieTable = ({ movieList }) => {
const [error, setError] = useState("")
async function handleClick(e, movieId) {
async function handleSubmit(e, movieId) {
e.preventDefault();
try {
setError("");
setError("")
await movieApi.submit(movieId)
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) {
catchErrors(error, setError)
}
}
return (
<table className={`table text-center ${styles.tableForm}`}>
<table className={`table text-center align-middle ${styles.tableForm}`}>
<thead className={`table-dark ${styles.dNone}`}>
<tr>
<th>제목</th>
<th>감독</th>
<th>상영일</th>
<th>줄거리</th>
<th>포스터</th>
<th>스틸컷</th>
<th>예고편</th>
<th className="col-md-5">제목</th>
<th className="col-md-4">감독</th>
<th className="col-md-3">상영일</th>
</tr>
</thead>
<tbody>
......@@ -35,24 +44,15 @@ const MovieTable = ({ movieList }) => {
<>
<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 data-label="- " className={`d-inline-block d-md-table-cell ${styles.data}`}>케이트 쇼트랜드</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.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>
<td className="d-none d-md-table-cell">{movie.director}</td>
<td className="d-none d-md-table-cell">{movie.release_date}</td>
</tr>
<tr className={styles.Row}>
<td colSpan="7" className="collapse" id={"movie" + movie.id}>
<div className={`d-inline-block d-md-none ${styles.word} mb-2`}>
줄거리 - {movie.overview !== '' ? 'O' : 'X'} /
포스터 - {movie.poster_path !== '' ? 'O' : 'X'} /
스틸컷 - {movie.backdrop_path !== '' ? 'O' : 'X'} /
예고편 - {movie.video !== false ? 'O' : 'X'}
</div>
<td colSpan="3" className="collapse" id={"movie" + movie.id}>
<div className={`d-inline-block d-md-none ${styles.word} mb-2`}>{movie.director} / {movie.release_date}</div>
<div className="d-flex justify-content-end">
<button type="button" className="btn btn-primary" onClick={(e) => handleClick(e, movie.id)}>등록</button>
{/* <button type="button" className="btn btn-danger">삭제</button> */}
{movie.existed ? <button type="button" className="btn btn-danger" onClick={(e) => handleDelete(e, movie.id)}>삭제</button>
: <button type="button" className="btn btn-primary" onClick={(e) => handleSubmit(e, movie.id)}>등록</button>}
</div>
</td>
</tr>
......
.Row:hover {
background: rgba(0, 0, 0, 0.075);
.Row {
cursor: pointer;
&:hover {
background: rgba(0, 0, 0, 0.075);
}
}
.word {
......@@ -17,11 +20,6 @@
& .Row {
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";
import Search from "../Search";
const MainNav = () => {
const [search, setSearch] = useState({ keyword: "" })
const [search, setSearch] = useState({ type: "home", keyword: "" })
const history = useHistory()
function searchMovie() {
......@@ -15,7 +15,7 @@ const MainNav = () => {
<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="/theater">극장</a>
<Search type="home" search={search} setSearch={setSearch} handleClick={searchMovie} />
<Search search={search} setSearch={setSearch} handleClick={searchMovie} />
</nav>
)
}
......
import styles from "./search.module.scss";
const Search = ({ type, search, setSearch, handleClick }) => {
const Search = ({ search, setSearch, handleClick }) => {
function handleSearch(e) {
const { name, value } = e.target
setSearch({ ...search, [name]: value })
}
console.log("type==", type)
return (
<div className="d-flex">
{type === "home" ? null :
<select className={`form-select ${styles.search}`} name="kind" aria-label="select search" onChange={handleSearch}>
<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>
<input className={`form-control ${search.type === "home" ? styles.searchWhite : `${styles.search}`}`} name="keyword" type="text" autoComplete="off" 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>
</div>
)
}
......
import { useLocation } from 'react-router-dom';
import queryString from 'query-string';
import MovieChart from "./MovieChart/index.js";
import { useState, useEffect } from 'react'
import { useLocation } from 'react-router-dom'
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 [result, setResult] = useState([])
const [error, setError] = useState("")
const { search } = useLocation()
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 (
<>
<h3 className="text-white text-center my-5">'{title}' 관한 검색 결과입니다.</h3>
<MovieChart />
{result.length !== 0 ? (
<>
<h3 className="text-white text-center my-5">'{title}' 관한 검색 결과입니다.</h3>
<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 MovieChart from '../components/MovieChart/MovieChart'
import MovieComming from '../components/MovieComming/MovieComming'
import MovieChart from '../components/MovieChart.js'
import MovieComming from '../components/MovieComming.js'
const MovieListPage = () => {
const [state, setState] = useState(true)
return (
<div className="container">
<div className="">
<div>
<ul className="nav nav-tabs justify-content-center my-4 border-0" id="myTab" role="tablist">
<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>
......
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) => {
console.log(elements)
res.json(elements)
} 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)=>{
const create = async (req, res) => {
try {
const { movieId } = req.params
const newMovie = await Movie.create({ movieId: movieId });
res.json(newMovie);
const { data } = await axios.get(`https://api.themoviedb.org/3/movie/${movieId}?api_key=${process.env.TMDB_APP_KEY}&language=ko-KR`)
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) {
res.status(500).send(error.message || "영화 등록 중 에러 발생");
return res.status(500).send(error.message || "영화 검색 중 에러 발생");
}
}
export default {
getMovieByCategory,
getMovieById,
getAllMovie,
getMovieList,
create,
remove,
findonlyTitle,
findaboutAll,
movieforAdmin
}
import { Sequelize } from "sequelize";
import UserModel from "../models/user.model.js";
import RoleModel from "../models/role.model.js";
import MovieModel from "../models/movie.model.js";
import CinemaModel from "../models/cinema.model.js";
import dbConfig from "../config/db.config.js";
const sequelize = new Sequelize(
......@@ -20,10 +22,17 @@ const sequelize = new Sequelize(
);
const User = UserModel(sequelize)
const Role = RoleModel(sequelize)
const Movie = MovieModel(sequelize)
const Cinema = CinemaModel(sequelize)
User.belongsTo(Role);
Role.hasOne(User);
export {
sequelize,
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) => {
type: DataTypes.INTEGER,
unique: true,
},
title: {
type: DataTypes.STRING,
},
release_date: {
type: DataTypes.STRING,
},
ticket_sales: {
type: DataTypes.FLOAT,
defaultValue: 0.0,
......
......@@ -2,12 +2,12 @@ import Sequelize from "sequelize";
const { DataTypes } = Sequelize;
// export const ROLE_NAME = {
// USER = "user",
// MEMBER = "member",
// ADMIN = "admin",
// ROOT = "root",
// }
export const ROLE_NAME = {
USER : "user",
MEMBER : "member",
ADMIN : "admin",
ROOT : "root",
}
const RoleModel = (sequelize) => {
const Role = sequelize.define(
......
......@@ -28,11 +28,6 @@ const UserModel = (sequelize) => {
phoneNumber: {
type: DataTypes.INTEGER
},
// role: {
// type: DataTypes.ENUM({
// values: Object.values(ROLE_NAME),
// }),
// }
},
{
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 userRouter from './user.route.js'
import movieRouter from './movie.route.js'
import cinemaRouter from "./cinema.route.js";
const router = express.Router();
router.use('/movie', movieRouter)
router.use('/auth', userRouter)
router.use('/cinema', cinemaRouter)
export default router;
\ No newline at end of file
......@@ -13,9 +13,26 @@ router.route('/showmovies/:category')
router.route('/movielist')
.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
.route("/:movieId")
.post(movieCtrl.create)
.delete(movieCtrl.remove)
router.param('category', movieCtrl.getMovieByCategory)
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