Commit ae80a05b authored by 백승민's avatar 백승민
Browse files

admin working

parent 4415469c
...@@ -2,7 +2,7 @@ import React from "react"; ...@@ -2,7 +2,7 @@ import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom"; import { BrowserRouter, Route, Routes } from "react-router-dom";
import "tailwindcss/tailwind.css"; import "tailwindcss/tailwind.css";
import { IntoPost } from "./post/intopost"; import { IntoPost } from "./post/intopost";
import { Login, Profile, RequireAuth, Signup } from "./auth"; import { Login, Profile, RequireAuth, Signup, Admin } from "./auth";
import { Header, Body } from "./home"; import { Header, Body } from "./home";
import { Board } from "./board"; import { Board } from "./board";
import Posting from "./post/posting"; import Posting from "./post/posting";
...@@ -35,6 +35,7 @@ export const App = () => { ...@@ -35,6 +35,7 @@ export const App = () => {
</RequireAuth> </RequireAuth>
} }
/> />
<Route path="admin" element={<Admin />} />
</Route> </Route>
</Route> </Route>
</Routes> </Routes>
......
import React, { useRef, useState } from "react"; import React, { useRef, useState } from "react";
type num = { type num = {
total: number, slides: any[],
page: number,
setPage: Function,
style: string,
setStyle: Function,
// slides : any[],
} }
export function MySlide({ total, page, setPage, style, setStyle}: num) { export function MySlide({ slides}: num) {
const numPages = Math.ceil(total / 15);
const firstLeftClick = useRef(true); const firstLeftClick = useRef(true);
const firstRightClick = useRef(true); const firstRightClick = useRef(true);
const slide = useRef(1); const [page, setPage] = useState(1);
const [slide,setSlide] = useState(1);
// const slide = useRef(1);
const [style, setStyle] = useState("");
const leftClick = () => { const leftClick = () => {
if (firstLeftClick.current) { if (firstLeftClick.current) {
...@@ -24,7 +20,8 @@ export function MySlide({ total, page, setPage, style, setStyle}: num) { ...@@ -24,7 +20,8 @@ export function MySlide({ total, page, setPage, style, setStyle}: num) {
} else { } else {
setPage(page - 1) setPage(page - 1)
} }
slide.current -= 1; // slide.current -= 1;
setSlide(slide-1)
setStyle("-translate-x-full animate-slidetoright"); setStyle("-translate-x-full animate-slidetoright");
}; };
...@@ -35,16 +32,30 @@ export function MySlide({ total, page, setPage, style, setStyle}: num) { ...@@ -35,16 +32,30 @@ export function MySlide({ total, page, setPage, style, setStyle}: num) {
} else { } else {
setPage(page + 1) setPage(page + 1)
} }
slide.current += 1; // slide.current += 1;
setSlide(slide+1)
setStyle("animate-slidetoleft"); setStyle("animate-slidetoleft");
}; };
return ( return (
<div> <div className="flex flex-row justify-center items-center ">
<button onClick={leftClick} disabled={slide.current === 1}> <button className="mx-3 w-6 h-6 rounded-full hover:bg-sky-100 hover:text-gray-400"onClick={leftClick} disabled={slide === 1}>
&lt;{slide.current} &lt;
{/* {slide.current} */}
</button> </button>
<button onClick={rightClick} disabled={slide.current === numPages}> <div
className={`m-3 md:m-5 md:basis-4/5 flex flex-row relative w-full overflow-hidden`}
>
{slides.slice(page - 1, page + 2).map((slide) => (
<div key={Math.random()} className="min-w-full"
>
<div key={slide} className={`inline-grid grid-cols-5 ${style}`}>
{slide}
</div>
</div>
))}
</div>
<button className="mx-3 w-6 h-6 rounded-full hover:bg-sky-100 hover:text-gray-400"onClick={rightClick} disabled={slide === slides.length}>
&gt; &gt;
</button> </button>
</div> </div>
...@@ -52,14 +63,3 @@ export function MySlide({ total, page, setPage, style, setStyle}: num) { ...@@ -52,14 +63,3 @@ export function MySlide({ total, page, setPage, style, setStyle}: num) {
); );
}; };
{/* {Array(numPages)
.fill(1)
.map((_, i) => (
<button key={i + 1} onClick={() => setPage(i + 1)}>
{i + 1}
</button>
))} */}
\ No newline at end of file
...@@ -3,3 +3,4 @@ import axios from "axios"; ...@@ -3,3 +3,4 @@ import axios from "axios";
export * as authApi from "./auth.api"; export * as authApi from "./auth.api";
export * as postApi from "./post.api"; export * as postApi from "./post.api";
export * as profileApi from "./profile.api" export * as profileApi from "./profile.api"
export * as mainimgApi from "./mainimg.api"
\ No newline at end of file
import axios from "axios";
import { MainimgType } from "../types";
import baseUrl from "./baseUrl";
export const mainimg = async (mainimg: MainimgType) => {
const { data } = await axios.post(`${baseUrl}/mainimg`, mainimg);
return data;
};
export const delmainimg = async (id : string) => {
const { data } = await axios.delete(`${baseUrl}/mainimg`);
return data;
};
export const getmainimg = async () => {
const { data } = await axios.get(`${baseUrl}/mainimg`);
console.log("test",data)
return data;
};
import React, { FormEvent, useEffect, useState } from "react";
import { mainimgApi } from "../apis";
import { catchErrors } from "../helpers";
import { MainimgType } from "../types";
export default function Admin() {
const [getimgs, setGetimgs] = useState<MainimgType[]>([]);
async function imgsData() {
const imgs = await mainimgApi.getmainimg();
console.log("ㅑㅡㅎ", imgs)
setGetimgs(imgs)
};
useEffect(() => {
imgsData();
}, []);
const [addimg, setAddimg] = useState<MainimgType>({
theme: "",
city: "",
url: "",
title: "",
});
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const [success, setSuccess] = useState(false);
function handleSelectChange(event: React.ChangeEvent<HTMLSelectElement>) {
const { name, value } = event.currentTarget;
setAddimg({ ...addimg, [name]: value });
}
function handleInputeChange(event: React.ChangeEvent<HTMLInputElement>) {
const { name, value } = event.currentTarget;
setAddimg({ ...addimg, [name]: value });
}
console.log("asdafsdfs", getimgs)
async function handleSubmit(event: FormEvent) {
event.preventDefault();
try {
setError("");
console.log("img data", addimg);
setLoading(true);
const res = await mainimgApi.mainimg(addimg);
console.log("서버연결됬나요", res);
setSuccess(true);
setError("");
} catch (error) {
console.log("에러발생");
catchErrors(error, setError);
} finally {
setLoading(false);
}
}
if (success) {
alert("img 추가되었습니다");
}
return (
<div>
<form
onSubmit={handleSubmit}>
<div className="flex justify-center gap-3">
<select
name="city"
id="Questions"
className="border border-3 border-black w-20 my-5"
defaultValue="질문종류"
onChange={handleSelectChange}
>
<option value="질문종류">도시</option>
<option value="Seoul">서울</option>
<option value="Busan">부산</option>
<option value="Incheon">인천</option>
<option value="Daegoo">대구</option>
<option value="Kwangjoo">광주</option>
<option value="Daejeon">대전</option>
<option value="Woolsan">울산</option>
<option value="Sejong">세종</option>
<option value="Dokdo">독도</option>
<option value="Jeju">제주</option>
</select>
<select
name="theme"
id="Questions"
className="border border-3 border-black w-20 my-5"
defaultValue="질문종류"
onChange={handleSelectChange}
>
<option value="질문종류">테마</option>
<option value="cycling">사이클링</option>
<option value="surfing">서핑</option>
<option value="activity">액티비티</option>
<option value="camping">캠핑</option>
<option value="sking">스키</option>
<option value="boat">보트</option>
<option value="desert">사막</option>
<option value="golf">골프</option>
<option value="cave">동굴</option>
<option value="history">문화재</option>
<option value="zoo">동물원</option>
<option value="cycling">사이클링</option>
</select>
<div>
<p>url</p>
<input name="url" className="border-2 border-sky-500"
onChange={handleInputeChange} />
{/* type="file"/> */}
</div>
<div>
<p>title</p>
<input name="title" className="border-2 border-sky-500"
onChange={handleInputeChange} />
</div>
<div className="my-5 ">
<button className="border-2 border-gray-500 rounded">추가</button>
</div>
</div>
</form>
{getimgs.map((img, index: number) => (
<div key = {index}>
<img src={img.url} />
</div>)
)}
</div>
);
};
...@@ -2,3 +2,4 @@ export { default as Login } from "./login"; ...@@ -2,3 +2,4 @@ export { default as Login } from "./login";
export { default as Signup } from "./signup"; export { default as Signup } from "./signup";
export { default as Profile } from "./profile"; export { default as Profile } from "./profile";
export { RequireAuth } from "./RequireAuth"; export { RequireAuth } from "./RequireAuth";
export { default as Admin } from "./admin";
...@@ -35,7 +35,11 @@ export default function Login() { ...@@ -35,7 +35,11 @@ export default function Login() {
setLoading(true); setLoading(true);
await login(user.email, user.password, () => { await login(user.email, user.password, () => {
if (user.email == "admin@korea.ac.kr" && user.password == "111111") {
navigate("/admin", { replace: true })
} else {
navigate("/", { replace: true }); navigate("/", { replace: true });
}
}); });
// console.log("서버연결됬나요", res); // console.log("서버연결됬나요", res);
// console.log("로그인"); // console.log("로그인");
......
...@@ -11,13 +11,6 @@ export default function Body() { ...@@ -11,13 +11,6 @@ export default function Body() {
let limit = 15; let limit = 15;
const [searchParams, setSearchParams] = useSearchParams(initSearchParams); const [searchParams, setSearchParams] = useSearchParams(initSearchParams);
const [page, setPage] = useState(1);
const [style, setStyle] = useState("");
const offset1 = (page - 1) * limit;
const offset2 = page * limit;
const offset3 = (page + 1) * limit;
let getPics = getPicure(); let getPics = getPicure();
useEffect(() => { useEffect(() => {
...@@ -31,6 +24,7 @@ export default function Body() { ...@@ -31,6 +24,7 @@ export default function Body() {
...Object.fromEntries(searchParams), ...Object.fromEntries(searchParams),
theme: event.currentTarget.id, theme: event.currentTarget.id,
}); });
// setPage(1)
}; };
const cityHandleClick = (event: MouseEvent<HTMLButtonElement>) => { const cityHandleClick = (event: MouseEvent<HTMLButtonElement>) => {
...@@ -55,104 +49,37 @@ export default function Body() { ...@@ -55,104 +49,37 @@ export default function Body() {
}); });
const numPages = Math.ceil(Idpics.length / 15); const numPages = Math.ceil(Idpics.length / 15);
console.log(page);
// const slides = []
// for (let i = 0; i < numPages + 1; i++) {
// const k =[
// <div key={Math.random()} className="min-w-full">
// <div className={`inline-grid grid-cols-5 ${style}`}>
// {Idpics.slice(i * limit, i * limit + limit).map((pic, index: number) => (
// <div
// className={`m-1 shrink-0 bg-gray-200 rounded shadow-md `}
// key={index}>
// <img
// src={pic.url}
// className="w-full h-10 md:h-20 object-center"
// />
// <p className="text-center text-xs">{pic.name}</p>
// </div>
// ))}
// </div>
// </div>]
// slides.push(k);
// }
return ( const slides = []
<div className="flex flex-col"> for (let i = 0; i < numPages; i++) {
<Theme handleClick={themeHandleClick} /> const k = [
<div className="flex flex-col md:flex-row py-5 "> Idpics.slice(i * limit, i * limit + limit).map((pic, index: number) => (
<div className="w-50">
<Citylist handleClick={cityHandleClick} />
</div>
<div className="flex flex-col overflow-hidden">
<MySlide
total={Idpics.length}
page={page}
setPage={setPage}
style={style}
setStyle={setStyle}
/>
<div
className={`md:mr-10 md:basis-4/5 flex flex-row relative w-full `}
>
<div key={Math.random()} className="min-w-full">
<div className={`inline-grid grid-cols-5 ${style}`}>
{Idpics.slice(offset1, offset1 + limit).map(
(pic, index: number) => (
<div
className={`m-1 shrink-0 bg-gray-200 rounded shadow-md `}
key={index}
>
<img
src={pic.url}
className="w-full h-10 md:h-20 object-center"
/>
<p className="text-center text-xs">{pic.name}</p>
</div>
)
)}
</div>
</div>
<div key={Math.random()} className="min-w-full">
<div className={`inline-grid grid-cols-5 ${style}`}>
{Idpics.slice(offset2, offset2 + limit).map(
(pic, index: number) => (
<div <div
className={`m-1 shrink-0 bg-gray-200 rounded shadow-md `} className={`m-1 shrink-0 bg-gray-200 rounded shadow-md `}
key={index} key={index}>
>
<img <img
src={pic.url} src={pic.url}
className="w-full h-10 md:h-20 object-center" className="w-full h-10 md:h-20 object-center"
/> />
<p className="text-center text-xs">{pic.name}</p> <p className="text-center text-xs">{pic.name}</p>
</div> </div>
) ))]
)} slides.push(k);
</div> }
return (
<div className="flex flex-col">
<Theme handleClick={themeHandleClick} />
<div className="flex flex-col md:flex-row py-5 ">
<div className="w-50">
<Citylist handleClick={cityHandleClick} />
</div> </div>
<div key={Math.random()} className="min-w-full"> <div className="flex flex-col">
<div className={`inline-grid grid-cols-5 ${style}`}> <MySlide key={Math.random()}
{Idpics.slice(offset3, offset3 + limit).map( slides={slides}
(pic, index: number) => (
<div
className={`m-1 shrink-0 bg-gray-200 rounded shadow-md `}
key={index}
>
<img
src={pic.url}
className="w-full h-10 md:h-20 object-center"
/> />
<p className="text-center text-xs">{pic.name}</p>
</div> </div>
)
)}
</div> </div>
</div>
</div>
</div>
</div>
<Outlet /> <Outlet />
</div> </div>
// Body Page // Body Page
......
...@@ -10,13 +10,13 @@ export default function Header() { ...@@ -10,13 +10,13 @@ export default function Header() {
<div className="flex flex-col "> <div className="flex flex-col ">
<div className="flex py-10 "> <div className="flex py-10 ">
<button className="shrink-0 mx-5"> <button className="shrink-0 mx-5">
<Link to="/" className="hover:bg-gray-200 focus:text-purple-500"> <Link to="/" className="hover:text-sky-300 focus:text-purple-500">
Travel Report Travel Report
</Link> </Link>
</button> </button>
<input <input
className="md:ml-20 placeholder:text-white focus:outline-none focus:border-y-4 focus:border-l-4 focus:border-sky-500 md:placeholder:text-slate-400 w-20 md:w-1/2 border-y-4 border-l-4 border-sky-500 pl-9 rounded-l-full focus:border-0" className="md:ml-20 placeholder:text-white focus:outline-none focus:border-y-4 focus:border-l-4 focus:border-sky-500 md:placeholder:text-slate-400 w-20 md:w-1/2 border-y-4 border-l-4 border-sky-300 pl-9 rounded-l-full focus:border-0"
placeholder="어디로 여행가고 싶나요?" placeholder="어디로 여행가고 싶나요?"
/> />
<button className="shrink-0 border-y-4 border-r-4 border-sky-500 rounded-r-full pr-4"> <button className="shrink-0 border-y-4 border-r-4 border-sky-500 rounded-r-full pr-4">
...@@ -45,7 +45,7 @@ export default function Header() { ...@@ -45,7 +45,7 @@ export default function Header() {
) : ( ) : (
<button className="shrink-0 bg-white "> <button className="shrink-0 bg-white ">
<Link <Link
className="hover:bg-purple-300 focus:text-purple-500" className="hover:text-sky-300 focus:text-purple-500"
to="/login" to="/login"
> >
로그인 로그인
...@@ -56,7 +56,7 @@ export default function Header() { ...@@ -56,7 +56,7 @@ export default function Header() {
<button className="shrink-0 bg-white"> <button className="shrink-0 bg-white">
<Link <Link
to="/board" to="/board"
className="hover:bg-purple-300 focus:text-purple-500" className="hover:text-sky-300 focus:text-purple-500"
> >
게시판 게시판
</Link> </Link>
......
...@@ -6,38 +6,38 @@ type ThemeProps = { ...@@ -6,38 +6,38 @@ type ThemeProps = {
export default function Theme({ handleClick }: ThemeProps) { export default function Theme({ handleClick }: ThemeProps) {
return ( return (
<div className="overflow-x-auto flex bg-emerald-400 rounded "> <div className="overflow-x-auto flex rounded md:justify-center">
<button id={"surfing"} onClick={handleClick} className="shrink-0 px-5"> <button id={"surfing"} onClick={handleClick} className="shrink-0 px-5 hover:text-sky-300">
서핑 서핑
</button> </button>
<button id={"activity"} onClick={handleClick} className="shrink-0 px-5"> <button id={"activity"} onClick={handleClick} className="shrink-0 px-5 hover:text-sky-300">
액티비티 액티비티
</button> </button>
<button id={"camping"} onClick={handleClick} className="shrink-0 px-5 "> <button id={"camping"} onClick={handleClick} className="shrink-0 px-5 hover:text-sky-300 ">
캠핑 캠핑
</button> </button>
<button id={"sking"} onClick={handleClick} className="shrink-0 px-5"> <button id={"sking"} onClick={handleClick} className="shrink-0 px-5 hover:text-sky-300">
스키 스키
</button> </button>
<button id={"boat"} onClick={handleClick} className="shrink-0 px-5"> <button id={"boat"} onClick={handleClick} className="shrink-0 px-5 hover:text-sky-300">
보트 보트
</button> </button>
<button id={"desert"} onClick={handleClick} className="shrink-0 px-5"> <button id={"desert"} onClick={handleClick} className="shrink-0 px-5 hover:text-sky-300">
사막 사막
</button> </button>
<button id={"golf"} onClick={handleClick} className="shrink-0 px-5"> <button id={"golf"} onClick={handleClick} className="shrink-0 px-5 hover:text-sky-300">
골프 골프
</button> </button>
<button id={"cave"} onClick={handleClick} className="shrink-0 px-5"> <button id={"cave"} onClick={handleClick} className="shrink-0 px-5 hover:text-sky-300">
동굴 동굴
</button> </button>
<button id={"history"} onClick={handleClick} className="shrink-0 px-5"> <button id={"history"} onClick={handleClick} className="shrink-0 px-5 hover:text-sky-300">
문화재 문화재
</button> </button>
<button id={"zoo"} onClick={handleClick} className="shrink-0 px-5"> <button id={"zoo"} onClick={handleClick} className="shrink-0 px-5 hover:text-sky-300">
동물원 동물원
</button> </button>
<button id={"cycling"} onClick={handleClick} className="shrink-0 px-5"> <button id={"cycling"} onClick={handleClick} className="shrink-0 px-5 hover:text-sky-300">
사이클링 사이클링
</button> </button>
</div> </div>
......
...@@ -6,8 +6,8 @@ type CityProps = { ...@@ -6,8 +6,8 @@ type CityProps = {
export default function Citylist({ handleClick }: CityProps) { export default function Citylist({ handleClick }: CityProps) {
return ( return (
<div className="overflow-auto w-full flex flex-row md:flex-col md:mr-24 bg-gray-400"> <div className="overflow-auto w-full flex flex-row md:flex-col md:mr-24 bg-sky-100">
<div className="text-center px-5 py-2 bg-red-400 shrink-0">도시</div> <div className="text-center px-5 py-2 bg-sky-300 shrink-0">도시</div>
<button <button
id={"Seoul"} id={"Seoul"}
onClick={handleClick} onClick={handleClick}
......
...@@ -36,3 +36,11 @@ export interface Profile { ...@@ -36,3 +36,11 @@ export interface Profile {
picturepath: string; picturepath: string;
nickname: string; nickname: string;
} }
export interface MainimgType {
theme: string;
city: string;
url: string;
title: string;
}
...@@ -2,3 +2,4 @@ export * as authCtrl from "./auth.controller"; ...@@ -2,3 +2,4 @@ export * as authCtrl from "./auth.controller";
export * as postCtrl from "./post.controller"; export * as postCtrl from "./post.controller";
export * as roleCtrl from "./role.controller"; export * as roleCtrl from "./role.controller";
export * as userCtrl from "./user.controller"; export * as userCtrl from "./user.controller";
export * as mainimgCtrl from "./mainimg.controller";
\ No newline at end of file
import isLength from "validator/lib/isLength";
import { TypedRequestAuth } from "./auth.controller";
import { asyncWrap } from "../helpers";
import { mainimgDb } from "../db";
export const createMainimg = asyncWrap(async (reqExp, res, next) => {
const req = reqExp as TypedRequestAuth<{ userId: string }>;
const { theme, city, url, title} = req.body as {
theme: string;
city: string;
url: string;
title: string;
};
console.log("body", req.body);
if (!isLength(url ?? "", { min: 1 })) {
return res.status(422).send("이미지 url을 입력해주세요");
}
if (!isLength(title ?? "", { min: 1 })) {
return res.status(422).send("이미지 제목을 입력해주세요");
}
const newMainimg = await mainimgDb.createMainimg({
theme,
city,
url,
title,
});
return res.json(newMainimg);
});
export const getMainimg = asyncWrap(async (req, res) => {
const mainimgs = await mainimgDb.getMainimg();
return res.json(mainimgs);
});
export * as roleDb from "./role.db"; export * as roleDb from "./role.db";
export * as postDb from "./post.db"; export * as postDb from "./post.db";
export * as userDb from "./user.db"; export * as userDb from "./user.db";
export * as mainimgDb from "./mainimg.db"
\ No newline at end of file
import { Mainimg, MainimgType } from "../models";
export const createMainimg = async (mainimg: MainimgType) => {
const newMainimg = await Mainimg.create({
theme: mainimg.theme,
city: mainimg.city,
url: mainimg.url,
title: mainimg.title,
});
return newMainimg;
};
export const getMainimg = async () => {
const users = await Mainimg.find({});
return users;
};
export { default as User, IUser } from "./user.model"; export { default as User, IUser } from "./user.model";
export { default as Post, PostType } from "./post.model"; export { default as Post, PostType } from "./post.model";
export { default as Role } from "./role.model"; export { default as Role } from "./role.model";
export { default as Mainimg, MainimgType} from "./mainimg.model"
\ No newline at end of file
import {model, Schema } from "mongoose";
export interface MainimgType {
theme: string;
city: string;
url: string;
title: string;
}
const MainimgSchema = new Schema<MainimgType>({
theme: {
type: String,
},
city: {
type: String,
},
url : {
type: String,
},
title: {
type: String,
required: true,
},
});
export default model<MainimgType>("Mainimg", MainimgSchema);
...@@ -3,6 +3,7 @@ import userRouter from "./user.route"; ...@@ -3,6 +3,7 @@ import userRouter from "./user.route";
import authRouter from "./auth.route"; import authRouter from "./auth.route";
import postRouter from "./post.route"; import postRouter from "./post.route";
import profileRouter from "./profile.route"; import profileRouter from "./profile.route";
import mainimgRouter from "./mainimg.route";
const router = express.Router(); const router = express.Router();
...@@ -10,6 +11,7 @@ router.use("/users", userRouter); ...@@ -10,6 +11,7 @@ router.use("/users", userRouter);
router.use("/auth", authRouter); router.use("/auth", authRouter);
router.use("/posts", postRouter); router.use("/posts", postRouter);
router.use("/profile", profileRouter); router.use("/profile", profileRouter);
router.use("/mainimg", mainimgRouter);
//posting함수 -> mongodb에 posts json형식으로 저장 //posting함수 -> mongodb에 posts json형식으로 저장
export default router; export default router;
import express from "express";
import { mainimgCtrl, authCtrl } from "../controllers";
const router = express.Router();
router
.route("/")
.get(authCtrl.requireLogin, mainimgCtrl.getMainimg)
.post(authCtrl.requireLogin, mainimgCtrl.createMainimg);
export default router;
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