Commit 91911f85 authored by Jiwon Yoon's avatar Jiwon Yoon
Browse files

Merge branch 'master' into jiwon

parents b2844cef 067778dd
......@@ -21,7 +21,7 @@ function App() {
return (
<div className="" style={{ backgroundColor: "black" }}>
<AuthProvider>
<Router style={{ backgroundColor: "black" }}>
<Router>
<SubNav />
<Header />
<MainNav />
......
import axios from "axios";
import {baseUrl} from "../utils/baseUrl.js";
import { baseUrl } from "../utils/baseUrl.js";
const getInfo = async () => {
const { data } = await axios.get(`${baseUrl}/api/cinema`)
const getCinemaInfo = async () => {
const { data } = await axios.get(`${baseUrl}/api/info/cinema`)
return data
}
const edit = async (cinemaInfo) => {
const { data } = await axios.put(`${baseUrl}/api/cinema`, cinemaInfo)
const editCinema = async (cinemaInfo) => {
const { data } = await axios.put(`${baseUrl}/api/info/cinema`, cinemaInfo)
return data
}
const getTicketFee = async () => {
const { data } = await axios.get(`${baseUrl}/api/info/ticketfee`)
return data
}
const getTicketFeeOne = async (theaterType) => {
const { data } = await axios.get(`${baseUrl}/api/info/ticketfee/${theaterType}`)
return data
}
const editTicketFee = async (ticketFeeInfo) => {
const { data } = await axios.put(`${baseUrl}/api/info/ticketfee`, ticketFeeInfo)
return data
}
const removeTicketFee = async (theaterType) => {
const { data } = await axios.delete(`${baseUrl}/api/info/ticketfee?theaterType=${theaterType}`)
return data
}
const cinemaApi = {
getInfo,
edit
getCinemaInfo,
editCinema,
getTicketFee,
getTicketFeeOne,
editTicketFee,
removeTicketFee
}
export default cinemaApi
\ No newline at end of file
import { useState, useEffect } from "react";
import { useState, useEffect, useRef } from "react";
import TicketEditForm from "./TicketEditForm.js";
import TicketFeeTable from "./TicketFeeTable.js";
import cinemaApi from "../../apis/cinema.api.js";
import catchErrors from "../../utils/catchErrors.js";
import styles from "./admin.module.scss";
......@@ -7,12 +9,15 @@ const INIT_CINEMAINFO = {
cinemaName: "",
transportation: "",
parking: "",
address: ""
address: "",
moreFeeInfo: ""
}
const CinemaEdit = () => {
const [cinemaInfo, setCinemaInfo] = useState(INIT_CINEMAINFO)
const [ticketFee, setTicketFee] = useState({})
const [error, setError] = useState("")
const formRef = useRef(null)
useEffect(() => {
getInfo()
......@@ -26,7 +31,7 @@ const CinemaEdit = () => {
async function getInfo() {
try {
setError("")
const info = await cinemaApi.getInfo()
const info = await cinemaApi.getCinemaInfo()
if (info) setCinemaInfo(info)
else setCinemaInfo(INIT_CINEMAINFO)
} catch (error) {
......@@ -37,7 +42,7 @@ const CinemaEdit = () => {
async function handleSubmit() {
try {
setError("")
await cinemaApi.edit(cinemaInfo)
await cinemaApi.editCinema(cinemaInfo)
window.location.reload()
} catch (error) {
catchErrors(error, setError)
......@@ -65,6 +70,14 @@ const CinemaEdit = () => {
<span className="input-group-text" id="address"><i className="bi bi-geo-alt-fill"></i></span>
<input type="text" className={`form-control ${styles.shadowNone}`} id="address" name="address" value={cinemaInfo.address} onChange={handleChange} value={cinemaInfo.address} />
</div>
<p className="mb-0">영화관람료 설정</p>
<p className="text-danger">*추가금액 정보를 입력바랍니다. 필요에 따라 기본가격 또한 변경 가능합니다.</p>
<TicketEditForm editFee={ticketFee} formRef={formRef} />
<TicketFeeTable setEditFee={setTicketFee} formRef={formRef} />
<div className="mb-3">
<label for="moreFeeInfo" className="form-label">관람료 추가정보</label>
<textarea className={`form-control ${styles.shadowNone} ${styles.textarea}`} rows="7" id="moreFeeInfo" name="moreFeeInfo" value={cinemaInfo.moreFeeInfo} onChange={handleChange}></textarea>
</div>
<div className="d-grid gap-2 mb-5">
<button type="submit" className={`btn btn-dark shadow-none ${styles.customBtn}`} onClick={handleSubmit}>수정</button>
</div>
......
import { useState, useEffect } from "react";
import cinemaApi from "../../apis/cinema.api.js";
import catchErrors from "../../utils/catchErrors.js";
import styles from "./admin.module.scss";
const INIT_TICKETFEE = {
theaterType: "",
weekdays: "",
weekend: "",
morning: "",
day: "",
night: "",
youth: "",
adult: "",
senior: "",
defaultPrice: 5000
}
const TicketEditForm = ({ editFee, formRef }) => {
const [ticketFee, setTicketFee] = useState(INIT_TICKETFEE)
const [error, setError] = useState("")
useEffect(() => {
setTicketFee({ ...ticketFee, ...editFee })
}, [editFee])
function handleChange(e) {
const { name, value } = e.target
setTicketFee({ ...ticketFee, [name]: value })
}
async function handleSubmit(e) {
e.preventDefault()
try {
setError("")
await cinemaApi.editTicketFee(ticketFee)
alert("해당 관람료 정보 등록이 성공적으로 완료되었습니다.")
window.location.reload()
} catch (error) {
catchErrors(error, setError)
}
}
return (
<form ref={formRef} onSubmit={handleSubmit}>
<div className="d-flex row row-cols-2 mb-2 mb-md-3 gx-0 gy-2 gy-md-0">
<label for="theaterType" className="col-md-auto col-form-label text-center text-md-start">상영관 종류</label>
<div className="col-md-4 col-lg-3 mx-md-2">
<input className={`form-control ${styles.shadowNone}`} type="text" id="theaterType" name="theaterType" value={ticketFee.theaterType} onChange={handleChange} />
</div>
<label for="defaultPrice" className="col-md-auto col-form-label text-center text-md-start">기본 가격</label>
<div className="col-md-3 col-lg-2 mx-md-2">
<input className={`form-control ${styles.shadowNone}`} type="number" id="defaultPrice" name="defaultPrice" value={ticketFee.defaultPrice} onChange={handleChange} />
</div>
</div>
<div className="d-flex row row-cols-2 mb-2 mb-md-3 gx-0 gy-2 gy-md-0">
<label for="weekdays" className="col-md-1 col-form-label text-center text-md-start">주중</label>
<div className="col-md-3 col-lg-2">
<input className={`form-control ${styles.shadowNone}`} type="number" id="weekdays" name="weekdays" value={ticketFee.weekdays} onChange={handleChange} />
</div>
<label for="weekend" className="col-md-1 col-form-label text-center">주말</label>
<div className="col-md-3 col-lg-2">
<input className={`form-control ${styles.shadowNone}`} type="number" id="weekend" name="weekend" value={ticketFee.weekend} onChange={handleChange} />
</div>
</div>
<div className="d-flex row row-cols-2 mb-2 mb-md-3 gx-0 gy-2 gy-md-0">
<label for="morning" className="col-md-1 col-form-label text-center text-md-start">조조</label>
<div className="col-md-3 col-lg-2">
<input className={`form-control ${styles.shadowNone}`} type="number" id="morning" name="morning" value={ticketFee.morning} onChange={handleChange} />
</div>
<label for="day" className="col-md-1 col-form-label text-center">일반</label>
<div className="col-md-3 col-lg-2">
<input className={`form-control ${styles.shadowNone}`} type="number" id="day" name="day" value={ticketFee.day} onChange={handleChange} />
</div>
<label for="night" className="col-md-1 col-form-label text-center">심야</label>
<div className="col-md-3 col-lg-2">
<input className={`form-control ${styles.shadowNone}`} type="number" id="night" name="night" value={ticketFee.night} onChange={handleChange} />
</div>
</div>
<div className="d-flex row row-cols-2 flex-wrap flex-lg-nowrap mb-2 mb-md-3 gx-0 gy-2 gy-md-0">
<label for="youth" className="col-md-1 col-form-label text-center text-md-start">청소년</label>
<div className="col-md-3 col-lg-2">
<input className={`form-control ${styles.shadowNone}`} type="number" id="youth" name="youth" value={ticketFee.youth} onChange={handleChange} />
</div>
<label for="adult" className="col-md-1 col-form-label text-center">일반</label>
<div className="col-md-3 col-lg-2">
<input className={`form-control ${styles.shadowNone}`} type="number" id="adult" name="adult" value={ticketFee.adult} onChange={handleChange} />
</div>
<label for="senior" className="col-md-1 col-form-label text-center">경로</label>
<div className="col-md-3 col-lg-2">
<input className={`form-control ${styles.shadowNone}`} type="number" id="senior" name="senior" value={ticketFee.senior} onChange={handleChange} />
</div>
<div className="col-12 col-md-2 col-lg-1 ms-lg-auto mt-md-2 mt-lg-0">
<button type="submit" className={`btn btn-dark w-100 ${styles.customBtn}`}>추가</button>
</div>
</div>
</form>
)
}
export default TicketEditForm
\ No newline at end of file
import { useState, useEffect } from "react";
import cinemaApi from "../../apis/cinema.api.js";
import catchErrors from "../../utils/catchErrors.js";
import styles from "./admin.module.scss";
const TicketFeeTable = ({ setEditFee, formRef }) => {
const [ticketFee, setTicketFee] = useState([])
const [error, setError] = useState("")
useEffect(() => {
getInfo()
}, [])
async function getInfo() {
const res = await cinemaApi.getTicketFee()
setTicketFee(res)
}
async function editRow(theaterType) {
try {
setError("")
const res = await cinemaApi.getTicketFeeOne(theaterType)
setEditFee({ ...res })
formRef?.current.scrollIntoView({ behavior: "smooth", block: "center" })
} catch (error) {
catchErrors(error, setError)
}
}
async function deleteData(theaterType) {
try {
setError("")
await cinemaApi.removeTicketFee(theaterType)
alert("해당 관람료 정보를 성공적으로 삭제했습니다.")
getInfo()
} catch (error) {
catchErrors(error, setError)
}
}
function priceToString(price) {
return price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
return (
<table className={`table caption-top text-center align-middle ${styles.tableForm}`}>
<caption className="text-dark">영화관람료 안내</caption>
<thead className={`table-dark align-middle ${styles.dNone}`}>
<tr>
<th className={styles.word}>상영관 종류</th>
<th>주중 / 주말</th>
<th>시간대</th>
<th>청소년</th>
<th>일반</th>
<th>경로</th>
<th style={{ width: "14%" }}></th>
</tr>
</thead>
<tbody>
{ticketFee.length !== 0 ? ticketFee.map(info =>
<>
<tr>
<td rowSpan="6" className={`d-block d-md-table-cell ${styles.Row} ${styles.type}`}>{info.theaterType}</td>
<td rowSpan="3" className={`d-block d-md-table-cell ${styles.Row} ${styles.moreData}`} data-label="- 청소년 / 성인 / 경로">주중(~)</td>
<td className="d-inline-block d-md-table-cell">조조 (06:00 ~ )</td>
<td className="d-inline-block d-md-table-cell">{priceToString(info.weekdays + info.morning + info.youth + info.defaultPrice)}</td>
<td className="d-inline-block d-md-table-cell">{priceToString(info.weekdays + info.morning + info.adult + info.defaultPrice)}</td>
<td className="d-inline-block d-md-table-cell">{priceToString(info.weekdays + info.morning + info.senior + info.defaultPrice)}</td>
<td rowSpan="6" className="d-none d-md-table-cell">
<div className="d-flex flex-column">
<button type="button" className="btn btn-primary my-1" onClick={() => editRow(info.theaterType)}>수정</button>
<button type="button" className="btn btn-danger my-1" onClick={() => deleteData(info.theaterType)}>삭제</button>
</div>
</td>
</tr>
<tr>
<td className="d-inline-block d-md-table-cell">일반 (11:00 ~ )</td>
<td className="d-inline-block d-md-table-cell">{priceToString(info.weekdays + info.day + info.youth + info.defaultPrice)}</td>
<td className="d-inline-block d-md-table-cell">{priceToString(info.weekdays + info.day + info.adult + info.defaultPrice)}</td>
<td className="d-inline-block d-md-table-cell">{priceToString(info.weekdays + info.day + info.senior + info.defaultPrice)}</td>
</tr>
<tr>
<td className="d-inline-block d-md-table-cell">심야 (00:00 ~ )</td>
<td className="d-inline-block d-md-table-cell">{priceToString(info.weekdays + info.night + info.youth + info.defaultPrice)}</td>
<td className="d-inline-block d-md-table-cell">{priceToString(info.weekdays + info.night + info.adult + info.defaultPrice)}</td>
<td className="d-inline-block d-md-table-cell">{priceToString(info.weekdays + info.night + info.senior + info.defaultPrice)}</td>
</tr>
<tr>
<td rowSpan="3" className={`d-block d-md-table-cell ${styles.Row} ${styles.moreData} ${styles.word}`} data-label="- 청소년 / 성인 / 경로">주말(~) 공휴일</td>
<td className="d-inline-block d-md-table-cell">조조 (06:00 ~ )</td>
<td className="d-inline-block d-md-table-cell">{priceToString(info.weekend + info.morning + info.youth + info.defaultPrice)}</td>
<td className="d-inline-block d-md-table-cell">{priceToString(info.weekend + info.morning + info.adult + info.defaultPrice)}</td>
<td className="d-inline-block d-md-table-cell">{priceToString(info.weekend + info.morning + info.senior + info.defaultPrice)}</td>
</tr>
<tr>
<td className="d-inline-block d-md-table-cell">일반 (11:00 ~ )</td>
<td className="d-inline-block d-md-table-cell">{priceToString(info.weekend + info.day + info.youth + info.defaultPrice)}</td>
<td className="d-inline-block d-md-table-cell">{priceToString(info.weekend + info.day + info.adult + info.defaultPrice)}</td>
<td className="d-inline-block d-md-table-cell">{priceToString(info.weekend + info.day + info.senior + info.defaultPrice)}</td>
</tr>
<tr className={styles.Row}>
<td className="d-inline-block d-md-table-cell">심야 (00:00 ~ )</td>
<td className="d-inline-block d-md-table-cell">{priceToString(info.weekend + info.night + info.youth + info.defaultPrice)}</td>
<td className="d-inline-block d-md-table-cell">{priceToString(info.weekend + info.night + info.adult + info.defaultPrice)}</td>
<td className="d-inline-block d-md-table-cell">{priceToString(info.weekend + info.night + info.senior + info.defaultPrice)}</td>
<td className={`d-block d-md-none ${styles.borderTop}`}>
<div className="d-flex justify-content-end">
<button type="button" className="btn btn-primary" onClick={() => editRow(info.theaterType)}>수정</button>
<button type="button" className="btn btn-danger ms-2" onClick={() => deleteData(info.theaterType)}>삭제</button>
</div>
</td>
</tr>
</>)
: <tr>
<td colSpan="7">등록된 관람료 관련 정보가 없습니다.</td>
</tr>}
</tbody>
</table>
)
}
export default TicketFeeTable
\ No newline at end of file
@media screen and (max-width: 768px) {
@media screen and (max-width: 767px) {
.box {
margin-bottom: 100px;
......@@ -14,6 +14,38 @@
z-index: 10;
}
}
.tableForm {
border-top: 2px solid;
& .dNone {
display: none;
border: 0;
}
& .Row {
border-bottom: 2px solid;
}
& .type {
font-size: 1.2rem;
background-color: #000;
color: #fff;
}
& .borderTop {
border-top: 1px solid;
}
& .moreData::after {
content: attr(data-label);
margin-left: 1em;
}
}
}
.word {
word-break: keep-all;
}
.shadowNone {
......
......@@ -31,7 +31,7 @@ const MovieTable = ({ movieList }) => {
}
return (
<table style={{ color:"white" }} className={`table text-center align-middle ${styles.tableForm}`}>
<table className={`table text-center align-middle ${styles.tableForm}`}>
<thead className={`table-dark ${styles.dNone}`}>
<tr>
<th className="col-md-5">제목</th>
......
......@@ -9,7 +9,7 @@
word-break: keep-all;
}
@media screen and (max-width: 768px) {
@media screen and (max-width: 767px) {
.tableForm {
border-top: 2px solid;
......
......@@ -7,11 +7,9 @@ const SubNav = () => {
<> {(user) ?
<nav className="nav justify-content-end py-1">
{(user.role === "member")
? <><Link className="nav-link text-white" to="/mypage">마이페이지</Link>
</>
? <Link className="nav-link text-white" to="/mypage">마이페이지</Link>
: <Link className="nav-link text-white" to="/admin">관리자페이지</Link>}
<Link className="nav-link text-white" to="/" onClick={logout}>로그아웃 </Link>
{/* <a className="nav-link text-white" href="/mypage" onClick={logout}>로그아웃</a> */}
</nav> :
<nav className="nav justify-content-end py-1">
<Link className="nav-link text-white" to="/login">로그인</Link>
......
......@@ -41,7 +41,7 @@ $theme-colors: map-merge($theme-colors, $custom-colors);
font-style: normal;
}
@media (max-width: 768px) {
@media (max-width: 767px) {
// .carousel-inner .carousel-item > div {
// display: none;
// }
......
......@@ -4,7 +4,7 @@ const getAll = async (req, res) => {
try {
const info = await Cinema.findOne({
where: { id: 1 },
attributes: ['cinemaName', 'transportation', 'parking', 'address']
attributes: ['cinemaName', 'transportation', 'parking', 'address', 'moreFeeInfo']
})
return res.json(info)
} catch (error) {
......
import { TicketFee } from "../db/index.js";
const getAll = async (req, res) => {
try {
const findAll = await TicketFee.findAll({ attributes: { exclude: ['createdAt', 'updatedAt'] } })
return res.json(findAll)
} catch (error) {
return res.status(500).send(error.message || "관람료 정보 가져오는 중 에러 발생")
}
}
const getOne = async (req, res) => {
try {
const { theaterType } = req.params
const find = await TicketFee.findOne({ where: { theaterType: theaterType }, attributes: { exclude: ['createdAt', 'updatedAt'] } })
if (!find) throw new Error("해당 정보를 찾지 못했습니다.");
return res.json(find)
} catch (error) {
return res.status(500).send(error.message || "관람료 정보 가져오는 중 에러 발생")
}
}
const edit = async (req, res) => {
try {
const { theaterType } = req.body
let response = null
const result = await TicketFee.findOrCreate({
where: { theaterType: theaterType },
defaults: { ...req.body }
})
if (!result[1]) {
const updateData = await TicketFee.update({ ...req.body }, { where: { theaterType: theaterType } })
response = updateData
} else response = result[0]
return res.json(response)
} catch (error) {
return res.status(500).send(error.message || "관람료 정보 수정 중 에러 발생")
}
}
const remove = async (req, res) => {
try {
const { theaterType } = req.query
const delNum = await TicketFee.destroy({ where: { theaterType: theaterType } })
if (delNum) res.json(delNum)
else throw new Error("해당 정보를 서버에서 삭제하는데 실패했습니다.");
} catch (error) {
return res.status(500).send(error.message || "관람료 정보 삭제 중 에러 발생")
}
}
export default {
getAll,
getOne,
edit,
remove
}
\ No newline at end of file
......@@ -4,6 +4,7 @@ import RoleModel from "../models/role.model.js";
import MovieModel from "../models/movie.model.js";
import CinemaModel from "../models/cinema.model.js";
import TheaterModel from "../models/theater.model.js";
import TicketFeeModel from "../models/ticketfee.model.js";
import TimeTableModel from '../models/role.model.js';
import ReservationModel from '../models/reservation.model.js';
import dbConfig from "../config/db.config.js";
......@@ -29,12 +30,15 @@ const Role = RoleModel(sequelize)
const Movie = MovieModel(sequelize)
const Cinema = CinemaModel(sequelize)
const Theater = TheaterModel(sequelize)
const TicketFee = TicketFeeModel(sequelize)
const TimeTable = TimeTableModel(sequelize)
const Reservation = ReservationModel(sequelize)
User.belongsTo(Role);
Role.hasOne(User);
TicketFee.hasOne(Theater, { foreignKey: "theaterType", targetKey: "theaterType", onDelete : "Cascade" });
export {
sequelize,
User,
......@@ -42,6 +46,7 @@ export {
Movie,
Cinema,
Theater,
TicketFee,
TimeTable,
Reservation
}
\ No newline at end of file
......@@ -22,6 +22,9 @@ const CinemaModel = (sequelize) => {
},
address: {
type: DataTypes.STRING
},
moreFeeInfo: {
type: DataTypes.TEXT
}
},
{
......
import Sequelize from "sequelize";
const { DataTypes } = Sequelize;
const TicketFeeModel = (sequelize) => {
const TicketFee = sequelize.define(
"ticketfee",
{
id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
theaterType: {
type: DataTypes.STRING
},
weekdays: {
type: DataTypes.INTEGER
},
weekend: {
type: DataTypes.INTEGER
},
morning: {
type: DataTypes.INTEGER
},
day: {
type: DataTypes.INTEGER
},
night: {
type: DataTypes.INTEGER
},
youth: {
type: DataTypes.INTEGER
},
adult: {
type: DataTypes.INTEGER
},
senior: {
type: DataTypes.INTEGER
},
defaultPrice: {
type: DataTypes.INTEGER,
}
},
{
timestamps: true,
freezeTableName: true,
tableName: "ticketfees"
}
);
return TicketFee;
};
export default TicketFeeModel;
\ No newline at end of file
import express from "express";
import cinemaCtrl from "../controllers/cinema.controller.js";
import ticketfeeCtrl from "../controllers/ticketfee.controller.js";
const router = express.Router();
router
.route("/")
.route("/cinema")
.get(cinemaCtrl.getAll)
.put(cinemaCtrl.edit)
router
.route("/ticketfee/:theaterType")
.get(ticketfeeCtrl.getOne)
router
.route("/ticketfee")
.get(ticketfeeCtrl.getAll)
.put(ticketfeeCtrl.edit)
.delete(ticketfeeCtrl.remove)
export default router;
\ No newline at end of file
......@@ -9,8 +9,8 @@ const router = express.Router();
router.use('/movie', movieRouter)
router.use('/auth', userRouter)
router.use('/cinema', cinemaRouter)
router.use('/kakaopay',kakaopayRouter)
router.use('/email',emailRouter)
router.use('/info', cinemaRouter)
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