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 { BrowserRouter, Route, Routes, Link, Outlet } from "react-router-dom";
import "tailwindcss/tailwind.css";
import { Login, Signup } from "./auth";
import { Login, Signup, Profile } from "./auth";
import { Board } from "./board";
import { Header, Body } from "./home";
import Posting from "./post/posting";
import Layout from "./commons/layout";
export const App = () => {
return (
<BrowserRouter>
<Routes>
<Route path="login" element={<Login />} />
<Route path="signup" element={<Signup />} />
<Route path="/" element={<Header />}>
<Route index element={<Body />} />
<Route path="board" element={<Board />} />
<Route path="posting" element={<Posting />} />
<Route element={<Layout />}>
<Route path="login" element={<Login />} />
<Route path="signup" element={<Signup />} />
<Route path="/" element={<Header />}>
<Route index element={<Body />} />
<Route path="board" element={<Board />} />
<Route path="posting" element={<Posting />} />
<Route path="profile" element={<Profile />} />
</Route>
</Route>
</Routes>
</BrowserRouter>
......
......@@ -2,3 +2,4 @@ import axios from "axios";
export * as authApi from "./auth.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 { IUser } from "../types";
import { IUser, Profile } from "../types";
import { profileApi } from "../apis";
const LOCAL_USER_INFO = "survey-user-info";
......@@ -58,3 +59,8 @@ export const getLocalUser = () => {
}
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 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";
import Theme from "./theme";
import Citylist from "../Pages/citylist";
import { getPicure } from "../Pages/pic";
import { PaginationLeft ,PaginationRight} from "../Pages/picpagination";
import { PaginationLeft, PaginationRight } from "../Pages/picpagination";
const initSearchParams = { theme: "", city: "" };
......@@ -48,45 +48,66 @@ export default function Body() {
);
});
const pre = () =>{
setSelected(selected -1)
};
const pre = () => {
setSelected(selected - 1);
};
const next = () =>{
setSelected(selected + 1)
};
const next = () => {
setSelected(selected + 1);
};
return (
<div className="flex flex-col px-1 py-1">
<div className="flex flex-col">
<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">
<Citylist handleClick={cityHandleClick} />
</div>
<div className="flex flex-col">
<div>
<button onClick={pre} disabled={selected === 1}>&lt;{selected}</button>
<button onClick={next} disabled={selected === 3}>&gt;</button>
<button onClick={pre} disabled={selected === 1}>
&lt;{selected}
</button>
<button onClick={next} disabled={selected === 3}>
&gt;
</button>
</div>
<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
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 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="m-1 shrink-0 bg-gray-200 rounded overflow-hidden shadow-md"
key={index}
......@@ -97,6 +118,11 @@ export default function Body() {
/>
<p className="text-center text-xs">{pic.name}</p>
</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 { useAuth } from "../auth/auth.context";
......@@ -8,56 +8,61 @@ export default function Header() {
const { logout } = useAuth();
return (
<div className="flex flex-col ">
<div className="flex flex-row px-5 py-20 md:place-content-between">
<button className="px-5 py-2">
<div className="flex py-10 ">
<button className="shrink-0 mx-5">
<Link to="/" className="hover:bg-gray-200 focus:text-purple-500">
Travel Report
</Link>
</button>
<div className="flex flex-row-reverse">
<div className="px-5 py-2 bg-teal-400 rounded">
{localStorage.getItem("survey-user-info") ? (
<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"
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
className="ml-2 mr-2"
onClick={() => {
logout();
}}
>
Logout
로그아웃
</button>
) : (
<button>
<Link to="/login">Login</Link>
</button>
)}
</div>
<button className="px-5 py-2 bg-purple-400 rounded">
<Link
to="/board"
className="hover:bg-purple-300 focus:text-purple-500 "
>
Board
</Link>
</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 className="border-0 border-r-2"></div>
<div></div>
</div>
) : (
<button className="shrink-0 bg-white ">
<Link
className="hover:bg-purple-300 focus:text-purple-500"
to="/login"
>
로그인
</Link>
</button>
)}
</div>
<button className="shrink-0 bg-white">
<Link
to="/board"
className="hover:bg-purple-300 focus:text-purple-500"
>
게시판
</Link>
</button>
</div>
<Outlet />
</div>
);
......
import React, { useEffect, MouseEvent, MouseEventHandler } from "react";
import { Outlet, useSearchParams } from "react-router-dom";
import React, { MouseEventHandler } from "react";
type ThemeProps = {
handleClick: MouseEventHandler;
}
};
export default function Theme({handleClick}: ThemeProps) {
export default function Theme({ handleClick }: ThemeProps) {
return (
<div className="overflow-x-scroll flex bg-emerald-400 rounded ">
<button id={"surfing"} onClick={handleClick} className="shrink-0 px-5">서핑</button>
<button id={"activity"} onClick={handleClick} className="shrink-0 px-5">액티비티</button>
<button id={"camping"} onClick={handleClick} className="shrink-0 px-5 ">캠핑</button>
<button id={"sking"} onClick={handleClick} className="shrink-0 px-5">스키</button>
<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 id={"cave"} onClick={handleClick} className="shrink-0 px-5">동굴</button>
<button id={"history"} onClick={handleClick} className="shrink-0 px-5">문화재</button>
<button id={"zoo"} onClick={handleClick} className="shrink-0 px-5">동물원</button>
<button id={"cycling"} onClick={handleClick} className="shrink-0 px-5">사이클링</button>
<div className="flex overflow-x-auto bg-emerald-400 md:px-52 ">
<button
id={"surfing"}
onClick={handleClick}
className="hover:underline underline-offset-4 decoration-white px-5 shrink-0"
>
서핑
</button>
<button
id={"activity"}
onClick={handleClick}
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>
);
};
\ No newline at end of file
}
import React from "react";
import { createRoot } from "react-dom/client";
import { App } from "./App";
import { AuthProvider } from "./auth/auth.context";
const container = document.getElementById("root");
const root = createRoot(container!);
root.render(
<React.StrictMode>
<AuthProvider>
<App />
</AuthProvider>
<App />
</React.StrictMode>
);
import React, { MouseEventHandler } from "react";
import React, { MouseEventHandler } from "react";
type CityProps = {
handleClick: MouseEventHandler;
}
};
export default function Citylist({handleClick}: CityProps) {
export default function Citylist({ handleClick }: CityProps) {
return (
<div className="overflow-auto flex flex-row mb-10 md:flex-col md:basis-1/5 md:mr-10 bg-gray-400 rounded ">
<button id={"Seoul"} onClick={handleClick} className="shrink-0 px-5 hover:underline">seoul</button>
<button id={"Busan"} onClick={handleClick} className="shrink-0 px-5 hover:underline">Busan</button>
<button id={"Incheon"} onClick={handleClick} className="shrink-0 px-5 hover:underline">Incheon</button>
<button id={"Daegoo"} onClick={handleClick} className="shrink-0 px-5 hover:underline">Daegoo</button>
<button id={"Kwangjoo"} onClick={handleClick} className="shrink-0 px-5 hover:underline">Kwangjoo</button>
<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 id={"Dokdo"} onClick={handleClick} className="shrink-0 px-5 hover:underline">Dokdo</button>
<button id={"Jeju"} onClick={handleClick} className="shrink-0 px-5 hover:underline">jeju</button>
<div className="overflow-auto w-full flex flex-row md:flex-col md:mr-24 bg-gray-400">
<div className="text-center px-5 py-2 bg-red-400 shrink-0">도시</div>
<button
id={"Seoul"}
onClick={handleClick}
className="px-5 py-2 hover:underline shrink-0"
>
서울
</button>
<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 */}
</div> // Citylist Page
</div>
// Citylist Page
);
};
\ No newline at end of file
}
......@@ -24,3 +24,8 @@ export interface SignupUser {
name: string;
password: string;
}
export interface Profile {
_id: string;
email: string;
}
import { userDb } from "../db";
import { asyncWrap } from "../helpers/asyncWrap";
import { Request } from "express";
export interface TypedRequestAuth<T> extends Request {
auth: T;
}
export const getUsers = asyncWrap(async (req, res) => {
const users = await userDb.getUsers();
......@@ -12,3 +17,11 @@ export const createUser = asyncWrap(async (req, res) => {
const newUser = await userDb.createUser(user);
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) => {
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";
import userRouter from "./user.route";
import authRouter from "./auth.route";
import postRouter from "./post.route";
import profileRouter from "./profile.route";
const router = express.Router();
router.use("/users", userRouter);
router.use("/auth", authRouter);
router.use("/posts", postRouter);
router.use("/profile", profileRouter);
//posting함수 -> mongodb에 posts json형식으로 저장
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