Commit d6ce49aa authored by Lee Soobeom's avatar Lee Soobeom
Browse files

Merge remote-tracking branch 'origin/MK12' into develop

parents 23965dce 59f5e72f
import React from "react"; import React from "react";
import { BrowserRouter, Route, Routes, Link, Outlet } from "react-router-dom"; import { BrowserRouter, Route, Routes, Link, Outlet } from "react-router-dom";
import "tailwindcss/tailwind.css"; import "tailwindcss/tailwind.css";
import { Login, Signup } from "./auth"; import { Login, Signup, Profile } from "./auth";
import { Board } from "./board"; import { Board } from "./board";
import { Header, Body } from "./home"; import { Header, Body } from "./home";
import Posting from "./post/posting"; import Posting from "./post/posting";
import Layout from "./commons/layout";
export const App = () => { export const App = () => {
return ( return (
<BrowserRouter> <BrowserRouter>
<Routes> <Routes>
<Route element={<Layout />}>
<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 />} />
<Route path="posting" element={<Posting />} /> <Route path="posting" element={<Posting />} />
<Route path="profile" element={<Profile />} />
</Route>
</Route> </Route>
</Routes> </Routes>
</BrowserRouter> </BrowserRouter>
......
...@@ -2,3 +2,4 @@ import axios from "axios"; ...@@ -2,3 +2,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"
\ No newline at end of file
import axios from "axios";
import baseUrl from "./baseUrl";
export const profile = async () => {
const { data } = await axios.get(`${baseUrl}/profile`);
return data;
};
\ No newline at end of file
import { authApi } from "../apis"; import { authApi } from "../apis";
import { IUser } from "../types"; import { IUser, Profile } from "../types";
import { profileApi } from "../apis";
const LOCAL_USER_INFO = "survey-user-info"; const LOCAL_USER_INFO = "survey-user-info";
...@@ -58,3 +59,8 @@ export const getLocalUser = () => { ...@@ -58,3 +59,8 @@ export const getLocalUser = () => {
} }
return user; return user;
}; };
export const handleProfile = async () => {
const user : Profile = await profileApi.profile();
return user
};
\ No newline at end of file
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;
};
export { default as Login } from "./login"; export { default as Login } from "./login";
export { default as Signup } from "./signup"; export { default as Signup } from "./signup";
export { default as Profile } from "./profile";
import React, { useEffect, useState } from "react";
import { handleProfile } from "./auth.helper";
export default function Profile() {
// 로컬 저장소에는 로그인 여부만 저장
const [email, setemail] = useState("");
const emailChange = async () => {
const profile = await handleProfile();
setemail(profile.email);
};
useEffect(() => {
emailChange();
}, []);
return (
<div className="grid ">
<div className="ml-20 mt-10">프로필 </div>
<div className="grid m-20 border-0 border-y-2">
<div className="flex">
<div className="py-10 basis-1/5 border-0 border-r-2 bg-gray-100 grid place-items-center ">
Email
</div>
<div className="basis-full">{email}</div>
</div>
<div className="flex border-0 border-t-2">
<div className="py-10 basis-1/5 border-0 border-r-2 bg-gray-100 grid place-items-center">
사진
</div>
<div className="basis-full">a</div>
</div>
<div className="flex border-0 border-t-2">
<div className="py-10 basis-1/5 border-0 border-r-2 bg-gray-100 grid place-items-center">
별명
</div>
<div className="basis-full">a</div>
</div>
</div>
</div>
);
}
import React, { ReactNode } from "react";
import { Outlet } from "react-router-dom";
import { AuthProvider } from "../auth/auth.context";
export default function Layout() {
return (
<AuthProvider>
<Outlet />
</AuthProvider>
);
}
...@@ -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 { PaginationLeft ,PaginationRight} from "../Pages/picpagination"; import { PaginationLeft, PaginationRight } from "../Pages/picpagination";
const initSearchParams = { theme: "", city: "" }; const initSearchParams = { theme: "", city: "" };
...@@ -48,32 +48,50 @@ export default function Body() { ...@@ -48,32 +48,50 @@ export default function Body() {
); );
}); });
const pre = () =>{ const pre = () => {
setSelected(selected -1) setSelected(selected - 1);
}; };
const next = () =>{ const next = () => {
setSelected(selected + 1) setSelected(selected + 1);
}; };
return ( return (
<div className="flex flex-col px-1 py-1"> <div className="flex flex-col">
<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-5 ">
<div className="w-50"> <div className="w-50">
<Citylist handleClick={cityHandleClick} /> <Citylist handleClick={cityHandleClick} />
</div> </div>
<div className="flex flex-col"> <div className="flex flex-col">
<div> <div>
<button onClick={pre} disabled={selected === 1}>&lt;{selected}</button> <button onClick={pre} disabled={selected === 1}>
<button onClick={next} disabled={selected === 3}>&gt;</button> &lt;{selected}
</button>
<button onClick={next} disabled={selected === 3}>
&gt;
</button>
</div> </div>
<div > <div>
<div className=" md:mr-10 md:basis-4/5 overflow-hidden"> <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 + '%)'}}> <div
<img className = "border-2"src="https://t1.daumcdn.net/cfile/tistory/9947E0365C31C1BF0E"/> className=" flex flex-row transition duration-500 relative"
<img className = "border-2"src="https://t1.daumcdn.net/cfile/tistory/9947E0365C31C1BF0E"/> style={{
<img className = "border-2"src="https://t1.daumcdn.net/cfile/tistory/9947E0365C31C1BF0E"/> 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> </div>
...@@ -85,8 +103,11 @@ export default function Body() { ...@@ -85,8 +103,11 @@ export default function Body() {
// Body Page // 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 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}
...@@ -97,6 +118,11 @@ export default function Body() { ...@@ -97,6 +118,11 @@ export default function Body() {
/> />
<p className="text-center text-xs">{pic.name}</p> <p className="text-center text-xs">{pic.name}</p>
</div> </div>
))} */} ))} */
{/* <PaginationLeft total={Idpics.length} page={page} setPage={setPage} /> */} }
{/* <PaginationRight total={Idpics.length} page={page} setPage={setPage} /> */} {
\ No newline at end of file /* <PaginationLeft total={Idpics.length} page={page} setPage={setPage} /> */
}
{
/* <PaginationRight total={Idpics.length} page={page} setPage={setPage} /> */
}
import React, { useState } from "react"; import React, { useReducer, useState } from "react";
import { Link, Outlet } from "react-router-dom"; import { Link, Outlet } from "react-router-dom";
import { useAuth } from "../auth/auth.context"; import { useAuth } from "../auth/auth.context";
...@@ -8,56 +8,61 @@ export default function Header() { ...@@ -8,56 +8,61 @@ export default function Header() {
const { logout } = useAuth(); 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 py-10 ">
<button className="px-5 py-2"> <button className="shrink-0 mx-5">
<Link to="/" className="hover:bg-gray-200 focus:text-purple-500"> <Link to="/" className="hover:bg-gray-200 focus:text-purple-500">
Travel Report Travel Report
</Link> </Link>
</button> </button>
<div className="flex flex-row-reverse">
<div className="px-5 py-2 bg-teal-400 rounded"> <input
{localStorage.getItem("survey-user-info") ? ( 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"
placeholder="어디로 여행가고 싶나요?"
/>
<button className="shrink-0 border-y-4 border-r-4 border-sky-500 rounded-r-full pr-4">
검색
</button>
<div className="shrink-0 p-3 md:ml-40 h-12">
{useAuth().user.isLoggedIn ? (
<div className="flex">
<Link to="/profile" className="mr-2 ">
프로필
</Link>
<div className="border-0 border-r-2"></div>
<div></div>
<button <button
className="ml-2 mr-2"
onClick={() => { onClick={() => {
logout(); logout();
}} }}
> >
Logout 로그아웃
</button> </button>
<div className="border-0 border-r-2"></div>
<div></div>
</div>
) : ( ) : (
<button> <button className="shrink-0 bg-white ">
<Link to="/login">Login</Link> <Link
className="hover:bg-purple-300 focus:text-purple-500"
to="/login"
>
로그인
</Link>
</button> </button>
)} )}
</div> </div>
<button className="shrink-0 bg-white">
<button className="px-5 py-2 bg-purple-400 rounded">
<Link <Link
to="/board" to="/board"
className="hover:bg-purple-300 focus:text-purple-500 " className="hover:bg-purple-300 focus:text-purple-500"
> >
Board 게시판
</Link> </Link>
</button> </button>
<div>
<label>
{/* <span className="sr-only">Search</span> */}
<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>
</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"
/>
</label>
</div>
</div>
</div> </div>
<Outlet /> <Outlet />
</div> </div>
); );
......
import React, { useEffect, MouseEvent, MouseEventHandler } from "react"; import React, { MouseEventHandler } from "react";
import { Outlet, useSearchParams } from "react-router-dom";
type ThemeProps = { type ThemeProps = {
handleClick: MouseEventHandler; handleClick: MouseEventHandler;
} };
export default function Theme({handleClick}: ThemeProps) { export default function Theme({ handleClick }: ThemeProps) {
return ( return (
<div className="overflow-x-scroll flex bg-emerald-400 rounded "> <div className="flex overflow-x-auto bg-emerald-400 md:px-52 ">
<button id={"surfing"} onClick={handleClick} className="shrink-0 px-5">서핑</button> <button
<button id={"activity"} onClick={handleClick} className="shrink-0 px-5">액티비티</button> id={"surfing"}
<button id={"camping"} onClick={handleClick} className="shrink-0 px-5 ">캠핑</button> onClick={handleClick}
<button id={"sking"} onClick={handleClick} className="shrink-0 px-5">스키</button> className="hover:underline underline-offset-4 decoration-white px-5 shrink-0"
<button id={"boat"} onClick={handleClick} className="shrink-0 px-5">보트</button> >
<button id={"desert"} onClick={handleClick} className="shrink-0 px-5">사막</button> 서핑
<button id={"golf"} onClick={handleClick} className="shrink-0 px-5">골프</button> </button>
<button id={"cave"} onClick={handleClick} className="shrink-0 px-5">동굴</button> <button
<button id={"history"} onClick={handleClick} className="shrink-0 px-5">문화재</button> id={"activity"}
<button id={"zoo"} onClick={handleClick} className="shrink-0 px-5">동물원</button> onClick={handleClick}
<button id={"cycling"} onClick={handleClick} className="shrink-0 px-5">사이클링</button> className="hover:underline underline-offset-4 decoration-white px-5 shrink-0"
>
액티비티
</button>
<button
id={"camping"}
onClick={handleClick}
className="hover:underline underline-offset-4 decoration-white px-5 shrink-0"
>
캠핑
</button>
<button
id={"sking"}
onClick={handleClick}
className="hover:underline underline-offset-4 decoration-white px-5 shrink-0"
>
스키
</button>
<button
id={"boat"}
onClick={handleClick}
className="hover:underline underline-offset-4 decoration-white px-5 shrink-0"
>
보트
</button>
<button
id={"desert"}
onClick={handleClick}
className="hover:underline underline-offset-4 decoration-white px-5 shrink-0"
>
사막
</button>
<button
id={"golf"}
onClick={handleClick}
className="hover:underline underline-offset-4 decoration-white px-5 shrink-0"
>
골프
</button>
<button
id={"cave"}
onClick={handleClick}
className="hover:underline underline-offset-4 decoration-white px-5 shrink-0"
>
동굴
</button>
<button
id={"history"}
onClick={handleClick}
className="hover:underline underline-offset-4 decoration-white px-5 shrink-0"
>
문화재
</button>
<button
id={"zoo"}
onClick={handleClick}
className="hover:underline underline-offset-4 decoration-white px-5 shrink-0"
>
동물원
</button>
<button
id={"cycling"}
onClick={handleClick}
className="hover:underline underline-offset-4 decoration-white px-5 shrink-0"
>
사이클링
</button>
</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, { MouseEventHandler } from "react"; import React, { MouseEventHandler } from "react";
type CityProps = { type CityProps = {
handleClick: MouseEventHandler; handleClick: MouseEventHandler;
} };
export default function Citylist({handleClick}: CityProps) { export default function Citylist({ handleClick }: CityProps) {
return ( return (
<div className="overflow-auto flex flex-row mb-10 md:flex-col md:basis-1/5 md:mr-10 bg-gray-400 rounded "> <div className="overflow-auto w-full flex flex-row md:flex-col md:mr-24 bg-gray-400">
<button id={"Seoul"} onClick={handleClick} className="shrink-0 px-5 hover:underline">seoul</button> <div className="text-center px-5 py-2 bg-red-400 shrink-0">도시</div>
<button id={"Busan"} onClick={handleClick} className="shrink-0 px-5 hover:underline">Busan</button> <button
<button id={"Incheon"} onClick={handleClick} className="shrink-0 px-5 hover:underline">Incheon</button> id={"Seoul"}
<button id={"Daegoo"} onClick={handleClick} className="shrink-0 px-5 hover:underline">Daegoo</button> onClick={handleClick}
<button id={"Kwangjoo"} onClick={handleClick} className="shrink-0 px-5 hover:underline">Kwangjoo</button> className="px-5 py-2 hover:underline shrink-0"
<button id={"Daejeon"} onClick={handleClick} className="shrink-0 px-5 hover:underline">Daejeon</button> >
<button id={"Woolsan"} onClick={handleClick} className="shrink-0 px-5 hover:underline">woolsan</button> 서울
<button id={"Sejong"} onClick={handleClick} className="shrink-0 px-5 hover:underline">Sejong</button> </button>
<button id={"Dokdo"} onClick={handleClick} className="shrink-0 px-5 hover:underline">Dokdo</button> <button
<button id={"Jeju"} onClick={handleClick} className="shrink-0 px-5 hover:underline">jeju</button> id={"Busan"}
onClick={handleClick}
className="px-5 py-2 hover:underline shrink-0"
>
부산
</button>
<button
id={"Incheon"}
onClick={handleClick}
className="px-5 py-2 hover:underline shrink-0"
>
인천
</button>
<button
id={"Daegoo"}
onClick={handleClick}
className="px-5 py-2 hover:underline shrink-0"
>
대구
</button>
<button
id={"Kwangjoo"}
onClick={handleClick}
className="px-5 py-2 hover:underline shrink-0"
>
광주
</button>
<button
id={"Daejeon"}
onClick={handleClick}
className="px-5 py-2 hover:underline shrink-0"
>
대전
</button>
<button
id={"Woolsan"}
onClick={handleClick}
className="px-5 py-2 hover:underline shrink-0"
>
울산
</button>
<button
id={"Sejong"}
onClick={handleClick}
className="px-5 py-2 hover:underline shrink-0"
>
세종
</button>
<button
id={"Dokdo"}
onClick={handleClick}
className="px-5 py-2 hover:underline shrink-0"
>
독도
</button>
<button
id={"Jeju"}
onClick={handleClick}
className="px-5 py-2 hover:underline shrink-0"
>
제주
</button>
{/* citylist */} {/* citylist */}
</div> // Citylist Page </div>
// Citylist Page
); );
}; }
\ No newline at end of file
...@@ -24,3 +24,8 @@ export interface SignupUser { ...@@ -24,3 +24,8 @@ export interface SignupUser {
name: string; name: string;
password: string; password: string;
} }
export interface Profile {
_id: string;
email: string;
}
import { userDb } from "../db"; import { userDb } from "../db";
import { asyncWrap } from "../helpers/asyncWrap"; import { asyncWrap } from "../helpers/asyncWrap";
import { Request } from "express";
export interface TypedRequestAuth<T> extends Request {
auth: T;
}
export const getUsers = asyncWrap(async (req, res) => { export const getUsers = asyncWrap(async (req, res) => {
const users = await userDb.getUsers(); const users = await userDb.getUsers();
...@@ -12,3 +17,11 @@ export const createUser = asyncWrap(async (req, res) => { ...@@ -12,3 +17,11 @@ export const createUser = asyncWrap(async (req, res) => {
const newUser = await userDb.createUser(user); const newUser = await userDb.createUser(user);
return res.json(newUser); return res.json(newUser);
}); });
export const getProfile = asyncWrap(async (reqExp, res) => {
const req = reqExp as TypedRequestAuth<{userId : string}>; // 앞에서는 토큰으로써 사용하기 때문에 JwtPayload 를 사용하고 여기서는 verify 에서 토큰을 디코딩했기에 ObjectId 타입의 string으로 바뀌게 된다.
const {userId} = req.auth;
const profile = await userDb.getProfile(userId);
res.json(profile)
})
\ No newline at end of file
...@@ -38,3 +38,8 @@ export const isUser = async (email: string) => { ...@@ -38,3 +38,8 @@ export const isUser = async (email: string) => {
return false; return false;
} }
}; };
export const getProfile = async (userId : string) => {
const profile = await User.findById(userId)
return profile //이름 수정
}
\ No newline at end of file
...@@ -2,11 +2,14 @@ import express from "express"; ...@@ -2,11 +2,14 @@ import express from "express";
import userRouter from "./user.route"; 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";
const router = express.Router(); 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.use("/posts", postRouter);
router.use("/profile", profileRouter);
//posting함수 -> mongodb에 posts json형식으로 저장
export default router; export default router;
import express from "express";
import { userCtrl, authCtrl } from "../controllers";
const router = express.Router();
router
.route("/")
.get(authCtrl.requireLogin, userCtrl.getProfile)
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