Commit 0ce1f449 authored by Yoon, Daeki's avatar Yoon, Daeki 😅
Browse files

Merge branch 'develop' into main-dev

parents a2cd7201 94bd52bc
...@@ -12,7 +12,6 @@ export const App = () => { ...@@ -12,7 +12,6 @@ export const App = () => {
<Routes> <Routes>
<Route path="login" element={<Login />} /> <Route path="login" element={<Login />} />
<Route path="signup" element={<Signup />} /> <Route path="signup" element={<Signup />} />
<Route path="/" element={<Header />}> <Route path="/" element={<Header />}>
<Route index element={<Body />} /> <Route index element={<Body />} />
<Route path="board" element={<Board />} /> <Route path="board" element={<Board />} />
......
import React from "react"; import React from "react";
type num = { type num = {
total: number, // total: number,
page: number, page: number,
setPage: Function setPage: Function
} }
export default function Pagination ({total, page, setPage} : num) { // export function PaginationLeft ({total, page, setPage} : num) {
const numPages = Math.ceil(total / 15); // const numPages = Math.ceil(total / 15);
export function PaginationLeft ({ page, setPage} : num) {
const numPages = 3
return ( return (
<div> <div>
<button onClick={() => setPage(page - 1)} disabled={page === 1}> <button onClick={() => setPage(page - 1)} disabled={page === 1}>
&lt; &lt;
</button> </button>
{Array(numPages) {/* {Array(numPages)
.fill(1) .fill(1)
.map((_, i) => ( .map((_, i) => (
<button key={i + 1} onClick={() => setPage(i + 1)}> <button key={i + 1} onClick={() => setPage(i + 1)}>
{i + 1} {i + 1}
</button> </button>
))} ))} */}
</div>
);
};
// export function PaginationRight ({total, page, setPage} : num) {
// const numPages = Math.ceil(total / 15);
export function PaginationRight ({ page, setPage} : num) {
const numPages = 3;
return (
<div>
<button onClick={() => setPage(page + 1)} disabled={page === numPages}> <button onClick={() => setPage(page + 1)} disabled={page === numPages}>
&gt; &gt;
</button> </button>
......
import axios from "axios"; import axios from "axios";
export * as authApi from "./auth.api"; export * as authApi from "./auth.api";
export * as postApi from "./post.api";
import axios from "axios";
import baseUrl from "./baseUrl";
import { PostingType } from "../types";
export const posting = async (post: PostingType) => {
const { data } = await axios.post(`${baseUrl}/posts/`, post);
return data;
};
import { authApi } from "../apis";
import { IUser } from "../types";
const LOCAL_USER_INFO = "survey-user-info";
/**
* 1. 백엔드 로그인을 호출하여 로그인 정보를 얻습니다.
* 2. 로컬 저장소에 저장합니다.
* 3. 사용자 정보를 반환합니다.
* @param email 이메일
* @param password 비밀번호
* @returns 사용자 정보
*/
export const handleLogin = async (email: string, password: string) => {
const user: IUser = await authApi.login(email, password);
// 로컬 저장소에는 로그인 여부만 저장
localStorage.setItem(
LOCAL_USER_INFO,
JSON.stringify({
isLoggedIn: user.isLoggedIn,
})
);
return user;
};
/**
* 로컬 저장소의 정보를 삭제합니다.
* 백엔드 로그아웃을 호출하여 쿠키를 제거합니다.
*/
export const handleLogout = async () => {
console.log("handle logout called");
localStorage.removeItem(LOCAL_USER_INFO);
try {
await authApi.logout();
} catch (error) {
console.log("logout 중에 에러 발생:", error);
}
};
/**
* 1. 로컬 저장소에 저장된 사용자 로그인 정보를 반환합니다.
* 2. 로컬 저장소에 정보가 없으면 { isLoggedIn: false }를 반환합니다.
* @returns 로컬 저장소에 저장된 사용자 정보
*/
export const getLocalUser = () => {
console.log("get local user called");
const userInfo = localStorage.getItem(LOCAL_USER_INFO);
const user: IUser = { isLoggedIn: false };
if (!userInfo) {
return user;
}
const userData = JSON.parse(userInfo);
if (userData.isLoggedIn) {
user.isLoggedIn = true;
} else {
user.isLoggedIn = false;
}
return user;
};
import { Link } from "react-router-dom"; import { Link, useNavigate } from "react-router-dom";
import React, { useState, FormEventHandler } from "react"; import React, { useState, useEffect, FormEvent } from "react";
import { LoginUser } from "../types";
import { catchErrors } from "../helpers";
import { useAuth } from "./auth.context";
interface login { export default function Login() {
id: string; const [user, setUser] = useState<LoginUser>({
password: string; email: "",
} password: "",
});
// const fake = { id: "asdf", password: "qwer" }; const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const [disabled, setDisabled] = useState(false);
const [success, setSuccess] = useState(false);
const navigate = useNavigate();
const { login } = useAuth();
function Logindata() { useEffect(() => {
const [id, setId] = useState(""); setDisabled(!(user.email && user.password));
const [password, setPassword] = useState(""); }, [user]);
function login() { function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
fetch(`http://localhost:3000/api/auth/login`, { const { name, value } = event.currentTarget;
method: "POST", setUser({ ...user, [name]: value });
}
async function handleSubmit(event: FormEvent) {
event.preventDefault();
try {
setError("");
console.log("user data", user);
body: JSON.stringify({ // setLoading(true);
email: `${id}`, await login(user.email, user.password, () => {
password: `${password}`, navigate("/", { replace: true });
}),
}).then((response) => {
console.log(response.json());
}); });
// console.log("서버연결됬나요", res);
// console.log("로그인");
// setSuccess(true);
// setError("");
} catch (error) {
console.log("에러발생");
// setError("이메일 혹은 비밀번호를 다시 입력해주세요.");
catchErrors(error, setError);
} finally {
setLoading(false);
}
} }
return ( return (
<div className="flex flex-col md:w-2/3 md:gap-2"> <div className="flex flex-col items-center my-10">
<div className="bg-white w-1/2 md:w-1/3 my-8 text-center text-2xl">
<Link to="/">Travel Report</Link>
</div>
<div className="flex flex-col w-full md:w-1/3 p-8 md:p-4 md:p-0">
<form onSubmit={handleSubmit}>
<div className="flex flex-col md:flex-row border-2 border-black rounded-xl p-8 md:p-12 gap-y-4 md:gap-x-6">
<div className="flex flex-col md:w-2/3 md:gap-2 ">
<input <input
className="placeholder:text-slate-300 className="placeholder:text-slate-300
bg-white border border-slate-500 rounded-2xl bg-white border border-slate-500 md:rounded-2xl
py-2 pl-9 pr-3 my-2 py-3 md:py-2 pl-9 pr-3
focus:border-black focus:border-black
" "
placeholder="Id" placeholder="이메일"
type="text" type="email"
name="Id" name="email"
onChange={(e) => setId(e.target.value)} onChange={handleChange}
/> />
<input <input
className="placeholder:italic placeholder:text-slate-300 className="placeholder:italic placeholder:text-slate-300
bg-white border border-slate-500 rounded-2xl bg-white border border-slate-500 md:rounded-2xl
py-2 pl-9 pr-3 py-3 md:py-2 pl-9 pr-3
focus:border-black focus:border-black
" "
placeholder="Password" placeholder="Password"
type="password" type="password"
name="Password" name="password"
onChange={(e) => setPassword(e.target.value)} onChange={handleChange}
/> />
</div>
<button <button
disabled={disabled}
type="submit" type="submit"
className="md:w-1/3 bg-sky-600 hover:bg-sky-700 rounded-xl" className="my-4 md:my-0 md:w-1/3 bg-sky-600 hover:bg-sky-700 rounded-xl text-xl py-4"
onClick={login}
> >
<Link to={"/"}>login</Link> login
</button> </button>
</div> </div>
); </form>
} <div className="flex justify-around m-4">
<button className="bg-white ">
export default function Login() {
return (
<div>
{/* <form onSubmit={loginsubmit}> */}
<div className="flex flex-row grid-rows-2">
<div className=" p-12 w-1/2 h-1/2 md:w-40 md:h-40 bg-red-400 place-self-center rounded-2xl">
<Link to="/">Travel Report</Link>
</div>
<div className=" flex-row w-auto h-60 md:w-1/2 bg-white border-2 border-black grid place-items-center rounded-xl place-self-center">
<div className="flex flex-col w-full md:flex-row md:p-20 md:gap-10">
<Logindata />
</div>
<div className="flex-row grid grid-cols-3">
<button className="bg-white bottom-0 right-0">
<Link to="/signup">회원가입</Link> <Link to="/signup">회원가입</Link>
</button> </button>
<div className="grid grid-cols-2 divide-x-2">
<div></div> <div></div>
<button className="bg-white inset-x-0"> <div></div>
</div>
<button className="bg-white">
<Link to="/forgot">비밀번호 찾기</Link> <Link to="/forgot">비밀번호 찾기</Link>
</button> </button>
</div> </div>
</div> </div>
</div> </div>
{/* </form> */}
</div> // Login Page
); );
} }
...@@ -14,6 +14,7 @@ interface Posts { ...@@ -14,6 +14,7 @@ interface Posts {
export const fakes = [ export const fakes = [
{ {
id: "a", id: "a",
username: "lsb",
title: "여행가고싶다...", title: "여행가고싶다...",
date: "2022-06-30", date: "2022-06-30",
counts: 0, counts: 0,
...@@ -22,6 +23,7 @@ export const fakes = [ ...@@ -22,6 +23,7 @@ export const fakes = [
}, },
{ {
id: "b", id: "b",
username: "lsb",
title: "바다!바다!바다!", title: "바다!바다!바다!",
date: "2022-08-01", date: "2022-08-01",
counts: 0, counts: 0,
...@@ -30,6 +32,7 @@ export const fakes = [ ...@@ -30,6 +32,7 @@ export const fakes = [
}, },
{ {
id: "c", id: "c",
username: "lsb",
title: "Jeju-island", title: "Jeju-island",
date: "2022-9-10", date: "2022-9-10",
counts: 0, counts: 0,
...@@ -38,6 +41,7 @@ export const fakes = [ ...@@ -38,6 +41,7 @@ export const fakes = [
}, },
{ {
id: "d", id: "d",
username: "lsb",
title: "마! 부싼 가봤나!", title: "마! 부싼 가봤나!",
date: "2022-9-22", date: "2022-9-22",
counts: 0, counts: 0,
...@@ -46,26 +50,29 @@ export const fakes = [ ...@@ -46,26 +50,29 @@ export const fakes = [
}, },
{ {
id: "e", id: "e",
username: "lsb",
title: "Daegu", title: "Daegu",
date: "2022-10-1", date: "2022-10-1",
counts: 0, counts: 0,
theme: "surfing", theme: "ski",
city: "seoul", city: "Daegu",
}, },
{ {
id: "f", id: "f",
username: "lsb",
title: "강원도 감자는 맛있다.", title: "강원도 감자는 맛있다.",
date: "2022-12-12", date: "2022-12-12",
counts: 0, counts: 0,
theme: "surfing", theme: "camping",
city: "seoul", city: "강원도",
}, },
{ {
id: "g", id: "g",
username: "lsb",
title: "부산남자의 서울여행", title: "부산남자의 서울여행",
date: "2022-12-25", date: "2022-12-25",
counts: 0, counts: 0,
theme: "surfing", theme: "activity",
city: "seoul", city: "seoul",
}, },
]; ];
......
...@@ -3,7 +3,7 @@ import { Outlet, useSearchParams } from "react-router-dom"; ...@@ -3,7 +3,7 @@ import { Outlet, useSearchParams } from "react-router-dom";
import Theme from "./theme"; import Theme from "./theme";
import Citylist from "../Pages/citylist"; import Citylist from "../Pages/citylist";
import { getPicure } from "../Pages/pic"; import { getPicure } from "../Pages/pic";
import Pagination from "../Pages/picpagination"; import { PaginationLeft ,PaginationRight} from "../Pages/picpagination";
const initSearchParams = { theme: "", city: "" }; const initSearchParams = { theme: "", city: "" };
...@@ -12,7 +12,7 @@ export default function Body() { ...@@ -12,7 +12,7 @@ export default function Body() {
const [searchParams, setSearchParams] = useSearchParams(initSearchParams); const [searchParams, setSearchParams] = useSearchParams(initSearchParams);
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const offset = (page - 1) * limit; const offset = (page - 1) * limit;
const [selected, setSelected] = useState(1);
let getPics = getPicure(); let getPics = getPicure();
useEffect(() => { useEffect(() => {
...@@ -48,13 +48,45 @@ export default function Body() { ...@@ -48,13 +48,45 @@ export default function Body() {
); );
}); });
const pre = () =>{
setSelected(selected -1)
};
const next = () =>{
setSelected(selected + 1)
};
return ( return (
<div className="flex flex-col px-1 py-1"> <div className="flex flex-col px-1 py-1">
<Theme handleClick={themeHandleClick} /> <Theme handleClick={themeHandleClick} />
<div className="flex flex-col md:flex-row py-10 "> <div className="flex flex-col md:flex-row py-10 ">
<div className="w-50">
<Citylist handleClick={cityHandleClick} /> <Citylist handleClick={cityHandleClick} />
<div className="flex-row md:mr-10 md:basis-4/5 grid grid-rows-3 grid-cols-5"> </div>
{Idpics.slice(offset, offset + limit).map((pic, index: number) => ( <div className="flex flex-col">
<div>
<button onClick={pre} disabled={selected === 1}>&lt;{selected}</button>
<button onClick={next} disabled={selected === 3}>&gt;</button>
</div>
<div >
<div className=" md:mr-10 md:basis-4/5 overflow-hidden">
<div className=" flex flex-row transition duration-500 relative" style={{"transform": 'translate(-' + (selected-1)*100 + '%)'}}>
<img className = "border-2"src="https://t1.daumcdn.net/cfile/tistory/9947E0365C31C1BF0E"/>
<img className = "border-2"src="https://t1.daumcdn.net/cfile/tistory/9947E0365C31C1BF0E"/>
<img className = "border-2"src="https://t1.daumcdn.net/cfile/tistory/9947E0365C31C1BF0E"/>
</div>
</div>
</div>
</div>
</div>
<Outlet />
</div>
// Body Page
);
}
{/* <div className=" md:mr-10 md:basis-4/5 grid grid-rows-3 grid-cols-5"></div> */}
{/* {Idpics.slice(offset, offset + limit).map((pic, index: number) => (
<div <div
className="m-1 shrink-0 bg-gray-200 rounded overflow-hidden shadow-md" className="m-1 shrink-0 bg-gray-200 rounded overflow-hidden shadow-md"
key={index} key={index}
...@@ -65,12 +97,6 @@ export default function Body() { ...@@ -65,12 +97,6 @@ export default function Body() {
/> />
<p className="text-center text-xs">{pic.name}</p> <p className="text-center text-xs">{pic.name}</p>
</div> </div>
))} ))} */}
<Pagination total={Idpics.length} page={page} setPage={setPage} /> {/* <PaginationLeft total={Idpics.length} page={page} setPage={setPage} /> */}
</div> {/* <PaginationRight total={Idpics.length} page={page} setPage={setPage} /> */}
</div> \ No newline at end of file
<Outlet />
</div>
// Body Page
);
}
import React from "react"; import React, { useState } from "react";
import {Link, Outlet} from "react-router-dom"; import { Link, Outlet } from "react-router-dom";
import { useAuth } from "../auth/auth.context";
import "tailwindcss/tailwind.css"; import "tailwindcss/tailwind.css";
export default function Header() { export default function Header() {
const { logout } = useAuth();
return ( return (
<div className="flex flex-col "> <div className="flex flex-col ">
<div className="flex flex-row px-5 py-20 md:place-content-between"> <div className="flex flex-row px-5 py-20 md:place-content-between">
<button className="px-5 py-2"> <button className="px-5 py-2">
<Link to="/" className="hover:bg-gray-200 focus:text-purple-500">Travel Report</Link> <Link to="/" className="hover:bg-gray-200 focus:text-purple-500">
Travel Report
</Link>
</button> </button>
<div className="flex flex-row-reverse"> <div className="flex flex-row-reverse">
<button className="px-5 py-2 bg-teal-400 rounded"> <div className="px-5 py-2 bg-teal-400 rounded">
<Link to="/login" className="hover:bg-teal-100 focus:text-purple-500 ">Login</Link> {localStorage.getItem("survey-user-info") ? (
<button
onClick={() => {
logout();
}}
>
Logout
</button>
) : (
<button>
<Link to="/login">Login</Link>
</button> </button>
)}
</div>
<button className="px-5 py-2 bg-purple-400 rounded"> <button className="px-5 py-2 bg-purple-400 rounded">
<Link to="/board" className="hover:bg-purple-300 focus:text-purple-500 ">Board</Link> <Link
to="/board"
className="hover:bg-purple-300 focus:text-purple-500 "
>
Board
</Link>
</button> </button>
<div> <div>
<label> <label>
{/* <span className="sr-only">Search</span> */} {/* <span className="sr-only">Search</span> */}
<span className="absolute inset-y-0 left-0 flex items-center pl-2"> <span className="absolute inset-y-0 left-0 flex items-center pl-2">
<svg className="h-5 w-5 fill-slate-300" viewBox="0 0 20 20"></svg> <svg
className="h-5 w-5 fill-slate-300"
viewBox="0 0 20 20"
></svg>
</span> </span>
<input className="placeholder:italic placeholder:text-slate-400 block bg-white w-full border border-slate-300 rounded-md py-2 pl-9 pr-3 shadow-sm focus:outline-none focus:border-sky-500 focus:ring-sky-500 focus:ring-1 sm:text-sm" placeholder="Search for anything..." type="text" name="search" /> <input
className="placeholder:italic placeholder:text-slate-400 block bg-white w-full border border-slate-300 rounded-md py-2 pl-9 pr-3 shadow-sm focus:outline-none focus:border-sky-500 focus:ring-sky-500 focus:ring-1 sm:text-sm"
placeholder="Search for anything..."
type="text"
name="search"
/>
</label> </label>
</div> </div>
</div> </div>
</div> </div>
<Outlet/> <Outlet />
</div> </div>
); );
} }
\ No newline at end of file
import React from "react"; import React from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import { App } from "./App"; import { App } from "./App";
import { AuthProvider } from "./auth/auth.context";
const container = document.getElementById("root"); const container = document.getElementById("root");
const root = createRoot(container!); const root = createRoot(container!);
root.render( root.render(
<React.StrictMode> <React.StrictMode>
<AuthProvider>
<App /> <App />
</AuthProvider>
</React.StrictMode> </React.StrictMode>
); );
import React, { useState } from "react"; import React, { FormEvent, useState } from "react";
import isLength from "validator/lib/isLength";
import equals from "validator/lib/equals";
import { catchErrors } from "../helpers";
import { PostingType } from "../types";
import { postApi } from "../apis";
function Title() { export default function Posting() {
const [title, setTitle] = useState<string>("질문종류"); const [city, setCity] = useState<string>("질문종류");
const [theme, setTheme] = useState<string>("질문종류");
const [title, setTitle] = useState<string>("");
const [text, setText] = useState<string>("");
function TitleChange(e: { target: { value: React.SetStateAction<string> } }) { const [user, setUser] = useState<PostingType>({
setTitle(e.target.value); title: "",
} text: "",
} theme: "",
city: "",
username: "",
});
function Body() { const [loading, setLoading] = useState(false);
const [body, setBody] = useState<string>("질문종류"); const [error, setError] = useState("");
const [disabled, setDisabled] = useState(false);
const [success, setSuccess] = useState(false);
function BodyChange(e: { target: { value: React.SetStateAction<string> } }) { async function handlePostSubmit(event: FormEvent) {
setBody(e.target.value); event.preventDefault(); // prevent onSubmit -> rerendering
try {
setError("");
console.log("user data", user);
if (postingFormMatch()) {
setLoading(true);
const res = await postApi.posting(user);
console.log("서버연결됬나요", res);
console.log("user save");
setSuccess(true);
setError("");
}
} catch (error) {
console.log("에러발생");
catchErrors(error, setError);
} finally {
setLoading(false);
}
} }
}
function SelectCity() {
const [selectCity, setSelectCity] = useState<string>("질문종류");
function CityChange(e: { target: { value: React.SetStateAction<string> } }) { function postingFormMatch() {
setSelectCity(e.target.value); if (!isLength(user.title ?? "", { min: 1 })) {
setError("제목을 입력해 주세요.");
return false;
} else if (!isLength(user.text ?? "", { min: 1 })) {
setError("내용을 입력해 주세요.");
return false;
} else if (equals(city, "질문종류")) {
setError("테마를 선택해 주세요.");
return false;
} else if (equals(theme, "질문종류")) {
setError("도시를 선택해 주세요.");
return false;
} else {
return true;
} }
}
const titleChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
const title = event.currentTarget.value;
const newUser = { ...user, title: title };
console.log(event.currentTarget.value);
setTitle(event.currentTarget.value);
setUser(newUser);
};
const textChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
const text = event.currentTarget.value;
const newUser = { ...user, text: text };
console.log(event.currentTarget.value);
setText(event.currentTarget.value);
setUser(newUser);
};
const cityChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
const city = event.currentTarget.value;
const newUser = { ...user, city: city };
console.log(event.currentTarget.value);
setCity(event.currentTarget.value);
setUser(newUser);
};
const themeChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
const theme = event.currentTarget.value;
const newUser = { ...user, theme: theme };
console.log(event.currentTarget.value);
setTheme(event.currentTarget.value);
setUser(newUser);
};
return ( return (
<div className="flex flex-col border-3">
<form onSubmit={handlePostSubmit} className="w-full items-center">
<div className="flex flex-row relative">
<p className="basis-1/12 gap-x-8">Id</p>
<p className="basis-8/12 invisible">empty</p>
<select <select
name="city"
id="Questions" id="Questions"
className="border border-3 border-black w-1/12" className="border border-3 border-black w-1/12"
onChange={CityChange} onChange={cityChange}
defaultValue="질문종류" defaultValue="질문종류"
> >
<option value="질문종류">도시</option> <option value="질문종류">도시</option>
...@@ -41,20 +120,11 @@ function SelectCity() { ...@@ -41,20 +120,11 @@ function SelectCity() {
<option value="Dokdo">독도</option> <option value="Dokdo">독도</option>
<option value="Jeju">제주</option> <option value="Jeju">제주</option>
</select> </select>
);
}
function SelectTheme() {
const [selectTheme, setSelectTheme] = useState<string>("질문종류");
function ThemeChange(e: { target: { value: React.SetStateAction<string> } }) {
setSelectTheme(e.target.value);
}
return (
<select <select
name="theme"
id="Questions" id="Questions"
className="border border-3 border-black w-1/12" className="border border-3 border-black w-1/12"
onChange={ThemeChange} onChange={themeChange}
defaultValue="질문종류" defaultValue="질문종류"
> >
<option value="질문종류">테마</option> <option value="질문종류">테마</option>
...@@ -70,32 +140,7 @@ function SelectTheme() { ...@@ -70,32 +140,7 @@ function SelectTheme() {
<option value="history">문화재</option> <option value="history">문화재</option>
<option value="zoo">동물원</option> <option value="zoo">동물원</option>
<option value="cycling">사이클링</option> <option value="cycling">사이클링</option>
<option value="cycling">{selectTheme}</option>
</select> </select>
);
}
// 눌렀다는 데이터가 어딘가에 있어야 한다. Map 객체를 이용해서 기타등등
// function postup() {
// axios.post("localhost:3000/api/post/up", {
// id: "a",
// title: title,
// body: body,
// date: `${() => new Date()}`,
// theme: selectTheme,
// city: selectCity,
// });
// }
export default function Posting() {
return (
<div className="flex flex-col border-3">
<form className="w-full items-center">
<div className="flex flex-row relative">
<p className="basis-1/12 gap-x-8">Id</p>
<p className="basis-8/12 invisible">empty</p>
<SelectCity />
<SelectTheme />
<button <button
type="submit" type="submit"
...@@ -107,13 +152,19 @@ export default function Posting() { ...@@ -107,13 +152,19 @@ export default function Posting() {
<div className="flex border-4"> <div className="flex border-4">
<textarea <textarea
onChange={Title} name="title"
onChange={titleChange}
placeholder="title" placeholder="title"
className="w-full h-8" className="w-full h-8"
></textarea> ></textarea>
</div> </div>
<div onChange={Body} className="flex border-2"> <div className="flex border-2">
<textarea placeholder="body" className="w-full h-96"></textarea> <textarea
onChange={textChange}
name="text"
placeholder="text"
className="w-full h-96"
></textarea>
</div> </div>
</form> </form>
</div> </div>
......
...@@ -4,14 +4,23 @@ export interface IUser { ...@@ -4,14 +4,23 @@ export interface IUser {
_id?: string; _id?: string;
} }
export interface PostType { export interface LoginUser {
id: string; email: string;
title: string; password: string;
body?: string; }
date: string;
export interface PostType extends PostingType {
date?: string;
counts: number; counts: number;
theme?: string; id?: string;
city?: string; }
export interface PostingType {
title: string;
text?: string;
theme: string;
city: string;
username: string;
} }
export interface SignupUser { export interface SignupUser {
......
import { NextFunction, Request, Response } from "express"; import { NextFunction, Request, Response } from "express";
import isLength from "validator/lib/isLength";
import equals from "validator/lib/equals";
import { asyncWrap } from "../helpers"; import { asyncWrap } from "../helpers";
import jwt, { JwtPayload } from "jsonwebtoken";
import { jwtCofig, envConfig, cookieConfig } from "../config";
import { postDb } from "../db"; import { postDb } from "../db";
export const posting = asyncWrap(async (req, res) => { export const posting = asyncWrap(async (req, res) => {
const { title, body, date, theme, city } = req.body; const { title, text, theme, city, username } = req.body;
console.log("body", req.body);
// 1) title 빈 문자열인지 확인 // 1) title 빈 문자열인지 확인
const titleString = postDb.checkTitleNull(title, ); if (!isLength(title ?? "", { min: 1 })) {
if (!titleString) { return res.status(422).send("제목을 한 글자 이상 입력해주세요");
return res.status(422).send(`${title} 제목을 입력해 주세요`);
} }
// 2) body 빈 문자열인지 확인 // 2) body 빈 문자열인지 확인
const bodyString = postDb.checkBodyNull(body, ); if (!isLength(text ?? "", { min: 1 })) {
if (!bodyString) { return res.status(422).send("제목을 한 글자 이상 입력해주세요");
return res.status(422).send(`${body} 여행 후기를 입력해 주세요`);
} }
// 3) submit 이벤트 발생시 date값 입력 // 3) theme dropdown default-value "테마"일 경우 에러
const dateGet = postDb.getSubmitDate(date, ); if (equals(theme, "질문종류")) {
return res.status(422).send("테마를 입력해 주세요");
// 4) theme dropdown default-value일 경우 에러
const themeSelect = postDb.selectTheme(theme, );
if (!themeSelect) {
return res.status(422).send(`${theme} 테마를 선택해 주세요`)
} }
// 5) cuty dropdown default-value일 경우 에러 // 4) city dropdown default-value "도시"일 경우 에러
const citySelect = postDb.selectCity(city, ); if (equals(city, "질문종류")) {
if (!citySelect) { return res.status(422).send("도시를 선택해 주세요");
return res.status(422).send(`${city} 도시를 선택해 주세요`)
} }
// 6) 토큰 생성
const token = jwt.sign({ }, jwtCofig.secret, {
expiresIn: jwtCofig.expires,
});
// 7) 쿠키에 토큰 저장 // 5) username 확인 필요 없음
res.cookie(cookieConfig.name, token, {
maxAge: cookieConfig.maxAge, // 6)
path: "/", const newPosting = await postDb.createPosting({
httpOnly: envConfig.mode === "production", title,
secure: envConfig.mode === "production", text,
theme,
city,
username,
}); });
return res.json(newPosting);
});
export const post = asyncWrap(async (req, res) => {
const { title, theme, city, username, date, counts } = req.body;
console.log("body", req.body);
// 1) DB postings에서 title, theme, city, username, date 불러오기
// 2) 불러온 데이터 + counts posts에 저장
// 8) 사용자 반환 // 3) DB posts에서 데이터 불러서 frontend로 보내기
res.json({});
}); });
import { PostType, Post } from "../models"; import { Posting, PostingType } from "../models";
import { Post, PostType } from "../models";
export const createPosting = async (posting: PostingType) => {
const newPosting = await Posting.create({
title: posting.title,
text: posting.text,
theme: posting.theme,
city: posting.city,
username: posting.username,
});
return newPosting;
};
export const createPost = async (post: PostType) => { export const createPost = async (post: PostType) => {
const newPost = await Post.create(post); const newPost = await Post.create({
return newPost; title: post.title,
theme: post.theme,
city: post.city,
date: post.date,
counts: post.counts,
});
}; };
export const checkTitleNull = async (
title : string,
) => {
}
export const checkBodyNull = async (
body : string,
) => {
}
export const getSubmitDate = async (
date : string,
) => {
}
export const selectTheme = async (
theme : string,
) => {
let user;
if( theme != "테마" ) {
}
}
export const selectCity = async (
city : string,
) => {
let user;
if ( city != "도시" ) {
}
}
export { default as User, IUser } from "./user.model"; export { default as User, IUser } from "./user.model";
export { default as Posting, PostingType } from "./posting.model";
export { default as Post, PostType } from "./post.model"; export { default as Post, PostType } from "./post.model";
import { model, Schema } from "mongoose"; import { model, Schema, Types } from "mongoose";
import { PostingType } from "./posting.model";
export interface PostType { export interface PostType extends PostingType {
id: string; date?: string;
title: string; counts: number;
date: Date; id?: string;
body : string; }
counts?: number;
theme: string;
city: string;
}
const schema = new Schema<PostType>({
id : {type: String},
title : { type: String },
date : {type: Date},
body : {type : String},
counts : {type: Number},
theme : {type: String},
city : {type: String},
const postSchema = new Schema<PostType>({
title: { type: String },
theme: { type: String },
city: { type: String },
username: { type: String },
date: { type: String },
counts: { type: Number },
}); });
export default model<PostType>("Post", schema); export default model<PostType>("Post", postSchema);
import { model, Schema } from "mongoose";
export interface PostingType {
title: string;
text?: string;
theme: string;
city: string;
username?: string;
}
const postingSchema = new Schema<PostingType>({
title: {
type: String,
required: true,
},
text: {
type: String,
required: true,
},
theme: {
type: String,
},
city: {
type: String,
},
username: {
type: String,
},
});
export default model<PostingType>("Posting", postingSchema);
...@@ -7,7 +7,7 @@ const router = express.Router(); ...@@ -7,7 +7,7 @@ const router = express.Router();
router.use("/users", userRouter); router.use("/users", userRouter);
router.use("/auth", authRouter); router.use("/auth", authRouter);
router.use("/posts", postRouter); //router.route router.use("/posts", postRouter);
//posting함수 -> mongodb에 posts json형식으로 저장
export default router; export default router;
...@@ -3,6 +3,6 @@ import { postCtrl } from "../controllers"; ...@@ -3,6 +3,6 @@ import { postCtrl } from "../controllers";
const router = express.Router(); const router = express.Router();
router.route("/posting").post(postCtrl.posting); router.route("/").post(postCtrl.posting);
export default router; 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