From 49d8a62f267b5a8bfa675612647d0093f2c5bc5e Mon Sep 17 00:00:00 2001 From: jiminChoi Date: Tue, 21 Jan 2025 15:52:55 +0900 Subject: [PATCH 001/104] =?UTF-8?q?[FE]=20FEAT:=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/App.tsx | 10 ++++++++-- v2/frontend/src/pages/ListPage.tsx | 6 ++++++ v2/frontend/src/pages/LoginPage.tsx | 6 ++++++ v2/frontend/src/{ => pages}/MainPage.tsx | 0 v2/frontend/src/pages/RegisterPage.tsx | 6 ++++++ v2/frontend/src/pages/SendPage.tsx | 6 ++++++ 6 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 v2/frontend/src/pages/ListPage.tsx create mode 100644 v2/frontend/src/pages/LoginPage.tsx rename v2/frontend/src/{ => pages}/MainPage.tsx (100%) create mode 100644 v2/frontend/src/pages/RegisterPage.tsx create mode 100644 v2/frontend/src/pages/SendPage.tsx diff --git a/v2/frontend/src/App.tsx b/v2/frontend/src/App.tsx index 84923e8..46d07b0 100644 --- a/v2/frontend/src/App.tsx +++ b/v2/frontend/src/App.tsx @@ -1,11 +1,17 @@ import { BrowserRouter, Route, Routes } from "react-router-dom"; -import MainPage from "./MainPage"; +import SendPage from "./pages/SendPage"; +import LoginPage from "./pages/LoginPage"; +import RegisterPage from "./pages/RegisterPage"; +import ListPage from "./pages/ListPage"; function App() { return ( - } /> + } /> + } /> + } /> + } /> ); diff --git a/v2/frontend/src/pages/ListPage.tsx b/v2/frontend/src/pages/ListPage.tsx new file mode 100644 index 0000000..7dd9991 --- /dev/null +++ b/v2/frontend/src/pages/ListPage.tsx @@ -0,0 +1,6 @@ +const ListPage = () => { + return
ListPage
; + }; + + export default ListPage; + \ No newline at end of file diff --git a/v2/frontend/src/pages/LoginPage.tsx b/v2/frontend/src/pages/LoginPage.tsx new file mode 100644 index 0000000..be0fb79 --- /dev/null +++ b/v2/frontend/src/pages/LoginPage.tsx @@ -0,0 +1,6 @@ +const LoginPage = () => { + return
LoginPage
; + }; + + export default LoginPage; + \ No newline at end of file diff --git a/v2/frontend/src/MainPage.tsx b/v2/frontend/src/pages/MainPage.tsx similarity index 100% rename from v2/frontend/src/MainPage.tsx rename to v2/frontend/src/pages/MainPage.tsx diff --git a/v2/frontend/src/pages/RegisterPage.tsx b/v2/frontend/src/pages/RegisterPage.tsx new file mode 100644 index 0000000..4741d19 --- /dev/null +++ b/v2/frontend/src/pages/RegisterPage.tsx @@ -0,0 +1,6 @@ +const RegisterPage = () => { + return
RegisterPage
; + }; + + export default RegisterPage; + \ No newline at end of file diff --git a/v2/frontend/src/pages/SendPage.tsx b/v2/frontend/src/pages/SendPage.tsx new file mode 100644 index 0000000..1832487 --- /dev/null +++ b/v2/frontend/src/pages/SendPage.tsx @@ -0,0 +1,6 @@ +const SendPage = () => { + return
send page
; + }; + + export default SendPage; + \ No newline at end of file From 4a1fce9d7ecd3aae0dfa4d2eebd0caa5d1541dc8 Mon Sep 17 00:00:00 2001 From: jiminChoi Date: Tue, 21 Jan 2025 17:02:31 +0900 Subject: [PATCH 002/104] =?UTF-8?q?[FE]=20FEAT:=20prettier=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/.prettierrc | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 v2/frontend/.prettierrc diff --git a/v2/frontend/.prettierrc b/v2/frontend/.prettierrc new file mode 100644 index 0000000..c81080a --- /dev/null +++ b/v2/frontend/.prettierrc @@ -0,0 +1,11 @@ +{ + "singleQuote": false, + "semi": true, + "useTabs": false, + "tabWidth": 2, + "printWidth": 80, + "bracketSpacing": true, + "arrowParens": "always", + "importOrderSeparation": false, + "importOrderSortSpecifiers": true +} From 7b4798b9b4eccbfcd4eb5eedba06f2ceefd16145 Mon Sep 17 00:00:00 2001 From: jiminChoi Date: Tue, 21 Jan 2025 17:14:14 +0900 Subject: [PATCH 003/104] [FE] FEAT: provider --- v2/frontend/src/App.tsx | 22 ++- v2/frontend/src/components/InputTextField.tsx | 0 v2/frontend/src/components/LoginTemplete.tsx | 29 +++ v2/frontend/src/components/PrivateRoute.tsx | 11 ++ v2/frontend/src/pages/MainPage.tsx | 5 - v2/frontend/src/pages/SendPage.tsx | 168 +++++++++++++++++- v2/frontend/src/routes.tsx | 54 ++++++ v2/package-lock.json | 6 + 8 files changed, 276 insertions(+), 19 deletions(-) create mode 100644 v2/frontend/src/components/InputTextField.tsx create mode 100644 v2/frontend/src/components/LoginTemplete.tsx create mode 100644 v2/frontend/src/components/PrivateRoute.tsx delete mode 100644 v2/frontend/src/pages/MainPage.tsx create mode 100644 v2/frontend/src/routes.tsx create mode 100644 v2/package-lock.json diff --git a/v2/frontend/src/App.tsx b/v2/frontend/src/App.tsx index 46d07b0..2337ca0 100644 --- a/v2/frontend/src/App.tsx +++ b/v2/frontend/src/App.tsx @@ -1,19 +1,23 @@ -import { BrowserRouter, Route, Routes } from "react-router-dom"; +import { BrowserRouter, Route, Routes, RouterProvider, createBrowserRouter } from "react-router-dom"; import SendPage from "./pages/SendPage"; import LoginPage from "./pages/LoginPage"; import RegisterPage from "./pages/RegisterPage"; import ListPage from "./pages/ListPage"; +import { routes } from './routes'; + +const route = createBrowserRouter(routes); function App() { return ( - - - } /> - } /> - } /> - } /> - - + // + // + // } /> + // } /> + // } /> + // } /> + // + // + ); } diff --git a/v2/frontend/src/components/InputTextField.tsx b/v2/frontend/src/components/InputTextField.tsx new file mode 100644 index 0000000..e69de29 diff --git a/v2/frontend/src/components/LoginTemplete.tsx b/v2/frontend/src/components/LoginTemplete.tsx new file mode 100644 index 0000000..96b6a20 --- /dev/null +++ b/v2/frontend/src/components/LoginTemplete.tsx @@ -0,0 +1,29 @@ +import styled from "styled-components"; + +const LoginTemplate = () => { + return ( + + CABI-Onboarding + {/* value={userId} + value={userPW} */} + + ); +}; + +const LoginPageStyled = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + height: 100%; +`; + +const LoginTitleStyled = styled.div` + font-size: 2rem; + font-weight: 700; + line-height: 3rem; +`; + +// const UserInputStyled = styled.input<{ isFocus: boolean }>` +// text-align: left; +// `; diff --git a/v2/frontend/src/components/PrivateRoute.tsx b/v2/frontend/src/components/PrivateRoute.tsx new file mode 100644 index 0000000..ad142e3 --- /dev/null +++ b/v2/frontend/src/components/PrivateRoute.tsx @@ -0,0 +1,11 @@ +const PrivateRoute = (props: any) => { + const { children } = props; + // const location = useLocation(); + + // auth fail + // return ; + + return children; +}; + +export default PrivateRoute; diff --git a/v2/frontend/src/pages/MainPage.tsx b/v2/frontend/src/pages/MainPage.tsx deleted file mode 100644 index 78ff93e..0000000 --- a/v2/frontend/src/pages/MainPage.tsx +++ /dev/null @@ -1,5 +0,0 @@ -const MainPage = () => { - return
main page
; -}; - -export default MainPage; diff --git a/v2/frontend/src/pages/SendPage.tsx b/v2/frontend/src/pages/SendPage.tsx index 1832487..b3a7fac 100644 --- a/v2/frontend/src/pages/SendPage.tsx +++ b/v2/frontend/src/pages/SendPage.tsx @@ -1,6 +1,164 @@ +import styled from "styled-components"; + const SendPage = () => { - return
send page
; - }; - - export default SendPage; - \ No newline at end of file + return ( + + +

알림

+
+ + 알림 보내기 + + + + 받는이(Intra ID/ Channel)* + + {/* */} + + + + 메시지 내용* + + + + 보내기 + + + +
+ ); +}; + +export default SendPage; + +const WrapperStyled = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 60px 0; +`; + +const TitleContainerStyled = styled.div` + width: 80%; + max-width: 1000px; + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 2px solid var(--service-man-title-border-btm-color); + margin-bottom: 70px; + font-weight: 700; + + .title { + font-size: 1.25rem; + letter-spacing: -0.02rem; + margin-bottom: 20px; + } +`; + +const ContainerStyled = styled.div` + width: 80%; + max-width: 1000px; + margin-bottom: 40px; +`; + +const SubTitleStyled = styled.h2` + font-size: 1.25rem; + font-weight: 700; + margin-bottom: 20px; +`; + +const CapsuleWrappingStyled = styled.div` + width: 100%; + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; +`; + +const CapsuleButtonStyled = styled.span<{ + channelBtnIsClicked?: boolean; + templateBtnIsClicked?: boolean; +}>` + display: flex; + justify-content: center; + align-items: center; + padding: 8px 20px; + background: var(--card-bg-color); + border: 1px solid var(--capsule-btn-border-color); + border-radius: 22px; + cursor: pointer; + +`; + +const FormWappingStyled = styled.div` + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; + background-color: var(--card-bg-color); + border-radius: 10px; + padding: 30px 20px; + gap: 20px; +`; + +const FormContainerStyled = styled.div` + width: 100%; +`; +const FormSubTitleStyled = styled.h3` + font-size: 0.875rem; + color: var(--gray-line-btn-color); + margin-bottom: 10px; + span { + color: var(--expired-color); + } +`; + +const FormTextareaStyled = styled.textarea` + color: var(--normal-text-color); + box-sizing: border-box; + width: 100%; + min-height: 200px; + background-color: var(--card-content-bg-color); + border-radius: 8px; + border: 1px solid var(--capsule-btn-border-color); + resize: none; + outline: none; + :focus { + border: 1px solid var(--sys-main-color); + } + text-align: left; + padding: 10px; + ::placeholder { + color: var(--line-color); + } +`; + +const FormButtonContainerStyled = styled.div` + width: 100%; + display: flex; + justify-content: space-between; +`; + +const FormButtonStyled = styled.button<{ primary?: boolean }>` + width: auto; + height: auto; + padding: 10px 16px; + font-size: 0.875rem; + background-color: ${(props) => + props.primary ? "var(--sys-main-color)" : "var(--card-content-bg-color)"}; + color: ${(props) => + props.primary + ? "var(--white-text-with-bg-color)" + : "var(--normal-text-color)"}; + font-weight: 700; + border: 1px solid var(--capsule-btn-border-color); + border-radius: 4px; + cursor: pointer; + :hover { + opacity: 0.85; + } +`; diff --git a/v2/frontend/src/routes.tsx b/v2/frontend/src/routes.tsx new file mode 100644 index 0000000..b5e0ba0 --- /dev/null +++ b/v2/frontend/src/routes.tsx @@ -0,0 +1,54 @@ +import SendPage from "./pages/SendPage"; +import LoginPage from "./pages/LoginPage"; +import RegisterPage from "./pages/RegisterPage"; +import ListPage from "./pages/ListPage"; +import { ReactNode } from "react"; +import PrivateRouteWrapper from "./components/PrivateRoute"; + +enum AccessType { + PUBLIC, + PRIVATE, +} + +export interface RouteInfo { + path: string; + accessType: AccessType; + element: ReactNode; +} + +const routesInfo: RouteInfo[] = [ + { + path: "login", + accessType: AccessType.PUBLIC, + element: , + }, + { + path: "list", + accessType: AccessType.PUBLIC, // PRIVATE + element: , + }, + { + path: "register", + accessType: AccessType.PUBLIC, // PRIVATE + element: , + }, + { + path: "send", + accessType: AccessType.PUBLIC, // PRIVATE + element: , + }, +]; + +const injectProtectedRoute = (routesInfo: RouteInfo[]) => { + return routesInfo.map((route: RouteInfo) => { + if (route.accessType === AccessType.PRIVATE) { + route.element = ( + {route.element} + ); + } + + return route; + }); +}; + +export const routes = injectProtectedRoute(routesInfo); diff --git a/v2/package-lock.json b/v2/package-lock.json new file mode 100644 index 0000000..316a452 --- /dev/null +++ b/v2/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "v2", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} From 017af9770ae2455dac50068ae644bebcaa7727cd Mon Sep 17 00:00:00 2001 From: Jihyun Kim Date: Tue, 21 Jan 2025 19:56:31 +0900 Subject: [PATCH 004/104] =?UTF-8?q?[FE]=20FEAT:=20reset-css=EC=9A=A9=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...{InputTextField.tsx => SendInputField.tsx} | 0 v2/frontend/src/index.css | 246 ++++++++++++++++++ 2 files changed, 246 insertions(+) rename v2/frontend/src/components/{InputTextField.tsx => SendInputField.tsx} (100%) create mode 100644 v2/frontend/src/index.css diff --git a/v2/frontend/src/components/InputTextField.tsx b/v2/frontend/src/components/SendInputField.tsx similarity index 100% rename from v2/frontend/src/components/InputTextField.tsx rename to v2/frontend/src/components/SendInputField.tsx diff --git a/v2/frontend/src/index.css b/v2/frontend/src/index.css new file mode 100644 index 0000000..60286cf --- /dev/null +++ b/v2/frontend/src/index.css @@ -0,0 +1,246 @@ +@import url("https://fonts.googleapis.com/css2?family=Do+Hyeon&display=swap&text=새롬관로그인중수요지식회여기엔사물함이없어4층은현재용불가입니다!"); +@import url("https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;700&display=swap"); + +:root { + /* white, gray, black */ + --ref-white: #ffffff; + --ref-gray-100: #f5f5f5; + --ref-gray-200: #eeeeee; + --ref-gray-300: #d9d9d9; + --ref-gray-400: #bcbcbc; + --ref-gray-500: #7b7b7b; + --ref-gray-600: #525252; + --ref-gray-700: #3c3c3c; + --ref-gray-800: #2e2e2e; + --ref-gray-900: #1f1f1f; + --ref-black: #181818; + + /* red */ + --ref-red-100: #ff4e4e; + --ref-red-200: #e54646; + + /* orange */ + --ref-orange-100: #ff8b5b; + --ref-orange-200: #ef8172; + + /* yellow */ + --ref-yellow: #ffc74c; + + /* green */ + --ref-green-100: #47ffa7; + --ref-green-200: #3fe596; + --ref-green-300: #00cec9; + --ref-green-400: #00c2ab; + + /* blue */ + --ref-blue-100: #f5f7ff; + --ref-blue-200: #dce7fd; + --ref-blue-300: #91b5fa; + --ref-blue-400: #7ebffb; + --ref-blue-500: #5278fd; + --ref-blue-600: #3f69fd; + --ref-blue-700: #2c5afd; + --ref-blue-800: #26447e; + --ref-blue-900: #252526; + + /* purple */ + --ref-purple-100: #f9f6ff; + --ref-purple-200: #dfd0fe; + --ref-purple-300: #a29bfe; + --ref-purple-400: #b18cff; + --ref-purple-500: #9747ff; + --ref-purple-600: #8337e5; + --ref-purple-700: #6931b2; + --ref-purple-800: #3c1c66; + --ref-purple-900: #252425; + + --ref-transparent-purple-100: #b18cff5f; + --ref-transparent-purple-200: #b08cffe1; + + /* pink */ + --ref-pink-100: #f473b1; + --ref-pink-200: #ff4589; + --ref-pink-300: #d72766; + + /* shadow color */ + --ref-black-shadow-100: rgba(0, 0, 0, 0.1); + --ref-black-shadow-200: rgba(0, 0, 0, 0.25); + --ref-black-shadow-300: rgba(0, 0, 0, 0.4); + --ref-black-shadow-400: rgba(0, 0, 0, 0.8); + + /* cabinet */ + --full-color: var(--ref-gray-200); + --banned-color: var(--ref-gray-700); + --broken-color: var(--ref-gray-700); + --session-color: var(--ref-gray-200); + + /* custom color */ + --custom-blue-100: var(--ref-blue-300); + --custom-blue-200: var(--ref-blue-500); + --custom-blue-300: var(--ref-blue-600); + --custom-pink-100: var(--ref-pink-100); + --custom-pink-200: var(--ref-pink-200); + --custom-pink-300: var(--ref-pink-300); + --custom-purple-100: var(--ref-purple-300); + --custom-purple-200: var(--ref-purple-500); + --custom-orange: var(--ref-orange-100); + --custom-yellow: var(--ref-yellow); + --custom-green-100: var(--ref-green-300); + --custom-green-200: var(--ref-green-400); + + /* component variable */ + --sys-sub-color: var(--ref-purple-400); + --sys-default-sub-color: var(--ref-purple-400); + --sys-presentation-sub-color: var(--ref-blue-300); + --tooltip-shadow-color: var(--ref-black-shadow-400); + --color-picker-border-shadow-color: var(--ref-black-shadow-200); + --presentation-detail-available-color: var(--ref-blue-200); + --presentation-blue-pagination-btn-color: var(--ref-blue-500); + --mine-text-color: var(--ref-black); + + /* font variable */ + --main-font: "Noto Sans KR", sans-serif; + --building-font: "Do Hyeon", sans-serif; + --size-base: 14px; + + /* default setting */ + font-family: "Noto Sans KR", Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; + display: swap; +} + +a { + color: var(--sys-main-color); + -webkit-tap-highlight-color: transparent; +} + +@media (hover: hover) and (pointer: fine) { + a:hover { + opacity: 0.9; + } +} + +html, +body { + width: 100%; + height: 100%; + user-select: none; + overflow: hidden; +} + +/* iOS Pinch Zoom disabled */ +body { + touch-action: none; +} + +#root { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + overflow: hidden; + + background-color: var(--bg-color); +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + cursor: pointer; + border: none; + background-color: var(--sys-main-color); + color: var(--white-text-with-bg-color); + width: 200px; + height: 60px; + font-size: 1.125rem; + text-align: center; + font-family: var(--main-font); + border-radius: 6px; + font-weight: 300; + -webkit-tap-highlight-color: transparent; +} + +@media (hover: hover) and (pointer: fine) { + button:hover { + opacity: 0.9; + } +} + +button:disabled { + cursor: wait; + opacity: 0.9; +} + +img { + -webkit-user-drag: none; + max-width: 100%; + width: 100%; + height: 100%; + vertical-align: top; + object-fit: cover; +} + +input { + color: var(--normal-text-color); + text-align: center; + border: none; + font-size: 1rem; + outline: none; + background: none; + box-sizing: border-box; +} + +.modal { + border-radius: 10px; + box-shadow: 10px 10px 40px 0px var(--login-card-border-shadow-color); +} + +.textNowrap { + text-overflow: ellipsis; + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} + +.noScrollbar { + -ms-overflow-style: none; + scrollbar-width: none; +} + +.noScrollbar::-webkit-scrollbar { + display: none; +} + +.blind { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + clip: rect(0 0 0 0); + overflow: hidden; +} + +.clear::after { + display: block; + clear: both; + content: ""; +} + +.leftNavButtonActive { + background: var(--sys-main-color); + color: var(--white-text-with-bg-color) !important; +} + +.cabiButton { + -webkit-tap-highlight-color: transparent; +} + +.domainButtonActive { + color: var(--sys-main-color) !important; +} From bb1e39bcad06519c6dc43f0ddcc5b0d53a7364b3 Mon Sep 17 00:00:00 2001 From: JunSeong Date: Tue, 21 Jan 2025 20:31:17 +0900 Subject: [PATCH 005/104] =?UTF-8?q?[FE]=20FEAT:=20=EB=9D=BC=EC=9A=B0?= =?UTF-8?q?=ED=8C=85=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/App.tsx | 22 +++--------- v2/frontend/src/components/PrivateRoute.tsx | 40 ++++++++++++++++++--- v2/frontend/src/routes.tsx | 24 ++++++------- 3 files changed, 50 insertions(+), 36 deletions(-) diff --git a/v2/frontend/src/App.tsx b/v2/frontend/src/App.tsx index 2337ca0..0ebb6e2 100644 --- a/v2/frontend/src/App.tsx +++ b/v2/frontend/src/App.tsx @@ -1,24 +1,10 @@ -import { BrowserRouter, Route, Routes, RouterProvider, createBrowserRouter } from "react-router-dom"; -import SendPage from "./pages/SendPage"; -import LoginPage from "./pages/LoginPage"; -import RegisterPage from "./pages/RegisterPage"; -import ListPage from "./pages/ListPage"; -import { routes } from './routes'; +import { RouterProvider, createBrowserRouter } from "react-router-dom"; +import { routes } from "./routes"; -const route = createBrowserRouter(routes); +const router = createBrowserRouter(routes); function App() { - return ( - // - // - // } /> - // } /> - // } /> - // } /> - // - // - - ); + return ; } export default App; diff --git a/v2/frontend/src/components/PrivateRoute.tsx b/v2/frontend/src/components/PrivateRoute.tsx index ad142e3..ac75b2d 100644 --- a/v2/frontend/src/components/PrivateRoute.tsx +++ b/v2/frontend/src/components/PrivateRoute.tsx @@ -1,9 +1,39 @@ -const PrivateRoute = (props: any) => { - const { children } = props; - // const location = useLocation(); +import { ReactElement, useEffect, useState } from "react"; +import { checkAuth } from "../api/users"; +import { Navigate, useLocation } from "react-router"; - // auth fail - // return ; +const PrivateRoute = ({ + children, +}: { + children: ReactElement; +}): ReactElement => { + const [isLoading, setIsLoading] = useState(true); + const [isAuthenticated, setIsAuthenticated] = useState(false); + const location = useLocation(); + + useEffect(() => { + // auth check - call "/user/auth" API with token + const checkAuthStatus = async () => { + try { + const res = await checkAuth(); + setIsAuthenticated(res.status === 200); + } catch (error) { + setIsAuthenticated(false); + } finally { + setIsLoading(false); + } + }; + + checkAuthStatus(); + }, []); + + if (isLoading) { + return
Loading...
; + } + + if (!isAuthenticated) { + return ; + } return children; }; diff --git a/v2/frontend/src/routes.tsx b/v2/frontend/src/routes.tsx index b5e0ba0..e0929e1 100644 --- a/v2/frontend/src/routes.tsx +++ b/v2/frontend/src/routes.tsx @@ -2,8 +2,8 @@ import SendPage from "./pages/SendPage"; import LoginPage from "./pages/LoginPage"; import RegisterPage from "./pages/RegisterPage"; import ListPage from "./pages/ListPage"; -import { ReactNode } from "react"; -import PrivateRouteWrapper from "./components/PrivateRoute"; +import { ReactElement } from "react"; +import PrivateRoute from "./components/PrivateRoute"; enum AccessType { PUBLIC, @@ -13,27 +13,27 @@ enum AccessType { export interface RouteInfo { path: string; accessType: AccessType; - element: ReactNode; + element: ReactElement; } const routesInfo: RouteInfo[] = [ { - path: "login", + path: "/", accessType: AccessType.PUBLIC, element: , }, { - path: "list", - accessType: AccessType.PUBLIC, // PRIVATE - element: , + path: "/register", + accessType: AccessType.PUBLIC, + element: , }, { - path: "register", + path: "/list", accessType: AccessType.PUBLIC, // PRIVATE - element: , + element: , }, { - path: "send", + path: "/send", accessType: AccessType.PUBLIC, // PRIVATE element: , }, @@ -42,9 +42,7 @@ const routesInfo: RouteInfo[] = [ const injectProtectedRoute = (routesInfo: RouteInfo[]) => { return routesInfo.map((route: RouteInfo) => { if (route.accessType === AccessType.PRIVATE) { - route.element = ( - {route.element} - ); + route.element = {route.element}; } return route; From a54ccb834ab9bb342c5058d4a3b3de9080dd208a Mon Sep 17 00:00:00 2001 From: JunSeong Date: Tue, 21 Jan 2025 20:33:08 +0900 Subject: [PATCH 006/104] =?UTF-8?q?[FE]=20FEAT:=20=EC=BB=A4=EC=8A=A4?= =?UTF-8?q?=ED=85=80=20axios=20=EC=9D=B8=EC=8A=A4=ED=84=B4=EC=8A=A4?= =?UTF-8?q?=EC=99=80=20API=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/api/axios.custom.ts | 27 +++++++++++++++++++++++++++ v2/frontend/src/api/messages.ts | 13 +++++++++++++ v2/frontend/src/api/users.ts | 21 +++++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 v2/frontend/src/api/axios.custom.ts create mode 100644 v2/frontend/src/api/messages.ts create mode 100644 v2/frontend/src/api/users.ts diff --git a/v2/frontend/src/api/axios.custom.ts b/v2/frontend/src/api/axios.custom.ts new file mode 100644 index 0000000..95311d4 --- /dev/null +++ b/v2/frontend/src/api/axios.custom.ts @@ -0,0 +1,27 @@ +import axios from "axios"; + +const axiosInstance = axios.create({ + baseURL: "http://localhost:3000/api", // 기본 URL + headers: { + "Content-Type": "application/json", + }, + withCredentials: true, +}); + +export const service = { + post: async (url: string, data: object) => { + return axiosInstance.post(url, data); + }, + + get: async (url: string, config?: { params?: object }) => { + return axiosInstance.get(url, config); + }, + + put: async (url: string, data: object) => { + return axiosInstance.put(url, data); + }, + + delete: async (url: string) => { + return axiosInstance.delete(url); + }, +}; diff --git a/v2/frontend/src/api/messages.ts b/v2/frontend/src/api/messages.ts new file mode 100644 index 0000000..d05b0cf --- /dev/null +++ b/v2/frontend/src/api/messages.ts @@ -0,0 +1,13 @@ +import { service } from "./axios.custom"; + +export const sendMessage = async (data: object) => { + return service.post(`/messages`, data); +}; + +export const getMessages = async (params: object) => { + return service.get(`/messages`, { params }); +}; + +export const updateMessage = async (userId: number, data: object) => { + return service.put(`/messages/${userId}`, data); +}; diff --git a/v2/frontend/src/api/users.ts b/v2/frontend/src/api/users.ts new file mode 100644 index 0000000..86c35d8 --- /dev/null +++ b/v2/frontend/src/api/users.ts @@ -0,0 +1,21 @@ +import { service } from "./axios.custom"; + +export const checkAuth = async () => { + return service.get("/users/auth"); +}; + +export const login = async (data: object) => { + return service.post("/users/login", data); +}; + +export const register = async (data: object) => { + return service.post("/users/register", data); +}; + +export const searchGroup = async (params: object) => { + return service.get(`/users/search/groups`, { params }); +}; + +export const searchName = async (params: object) => { + return service.get(`/users/search/names`, { params }); +}; From 6b8192d5340b107cc2a60f3752b4e0ae0b06a7de Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Tue, 21 Jan 2025 20:39:50 +0900 Subject: [PATCH 007/104] =?UTF-8?q?[FE]=20REMOVE:=20LoginTemplete.tsx=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/components/LoginTemplete.tsx | 29 -------------------- 1 file changed, 29 deletions(-) delete mode 100644 v2/frontend/src/components/LoginTemplete.tsx diff --git a/v2/frontend/src/components/LoginTemplete.tsx b/v2/frontend/src/components/LoginTemplete.tsx deleted file mode 100644 index 96b6a20..0000000 --- a/v2/frontend/src/components/LoginTemplete.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import styled from "styled-components"; - -const LoginTemplate = () => { - return ( - - CABI-Onboarding - {/* value={userId} - value={userPW} */} - - ); -}; - -const LoginPageStyled = styled.div` - display: flex; - justify-content: space-between; - align-items: center; - width: 100%; - height: 100%; -`; - -const LoginTitleStyled = styled.div` - font-size: 2rem; - font-weight: 700; - line-height: 3rem; -`; - -// const UserInputStyled = styled.input<{ isFocus: boolean }>` -// text-align: left; -// `; From f570b245157c17c2348b310179c54cec0d7776b1 Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Tue, 21 Jan 2025 20:40:25 +0900 Subject: [PATCH 008/104] =?UTF-8?q?[FE]=20FIX:=20LoginPage=20=EB=A5=BC=20?= =?UTF-8?q?=EA=B8=B0=EB=B3=B8=20=EA=B2=BD=EB=A1=9C=EB=A1=9C=20=EC=A7=80?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/routes.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/frontend/src/routes.tsx b/v2/frontend/src/routes.tsx index b5e0ba0..afaf6a4 100644 --- a/v2/frontend/src/routes.tsx +++ b/v2/frontend/src/routes.tsx @@ -18,7 +18,7 @@ export interface RouteInfo { const routesInfo: RouteInfo[] = [ { - path: "login", + path: "/", accessType: AccessType.PUBLIC, element: , }, From 2e187c3397161bfbeb3ca6691d5c97d1c5f320de Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Tue, 21 Jan 2025 20:41:29 +0900 Subject: [PATCH 009/104] =?UTF-8?q?[FE]=20FIX:=20RouterProvier=20=EC=9D=98?= =?UTF-8?q?=20route=EB=A5=BC=20router=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/App.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/v2/frontend/src/App.tsx b/v2/frontend/src/App.tsx index 2337ca0..4cd8549 100644 --- a/v2/frontend/src/App.tsx +++ b/v2/frontend/src/App.tsx @@ -1,9 +1,15 @@ -import { BrowserRouter, Route, Routes, RouterProvider, createBrowserRouter } from "react-router-dom"; +import { + BrowserRouter, + Route, + Routes, + RouterProvider, + createBrowserRouter, +} from "react-router-dom"; import SendPage from "./pages/SendPage"; import LoginPage from "./pages/LoginPage"; import RegisterPage from "./pages/RegisterPage"; import ListPage from "./pages/ListPage"; -import { routes } from './routes'; +import { routes } from "./routes"; const route = createBrowserRouter(routes); @@ -17,7 +23,7 @@ function App() { // } /> // // - + ); } From cc94a6730340e0297e436997fbd414e6bfe440c3 Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Tue, 21 Jan 2025 20:42:00 +0900 Subject: [PATCH 010/104] =?UTF-8?q?[FE]=20FEAT:=20LoginInputField=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/LoginInputField.tsx | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 v2/frontend/src/components/LoginInputField.tsx diff --git a/v2/frontend/src/components/LoginInputField.tsx b/v2/frontend/src/components/LoginInputField.tsx new file mode 100644 index 0000000..683c961 --- /dev/null +++ b/v2/frontend/src/components/LoginInputField.tsx @@ -0,0 +1,53 @@ +import React, { useState } from "react"; +import styled from "styled-components"; + +type LoginInputFieldProps = { + value: string; + onChange: (e: React.ChangeEvent) => void; + placeholder?: string; +}; + +const LoginInputField: React.FC = ({ + value, + onChange, + placeholder, +}) => { + const [isFocused, setIsFocused] = useState(false); + + return ( + + setIsFocused(true)} + onBlur={() => setIsFocused(false)} + /> + + ); +}; + +const LoginInputFieldWrapper = styled.div` + display: flex; + justify-content: center; + align-items: center; + width: 100%; + margin-bottom: 1rem; +`; + +const LoginInputFieldStyled = styled.input<{ $isFocus: boolean }>` + width: 100%; + padding: 0.5rem 1rem; + font-size: 1rem; + border: 2px solid ${({ $isFocus }) => ($isFocus ? "#9747ff" : "#D7D7D7")}; + border-radius: 4px; + outline: none; + transition: border-color 0.3s; + + &::placeholder { + color: #858486; + } +`; + +export default LoginInputField; From b96c82362da6eae24bdff112afeb09055f8f7a8a Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Tue, 21 Jan 2025 20:42:28 +0900 Subject: [PATCH 011/104] =?UTF-8?q?[FE]=20FEAT:=20LoginPage=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/pages/LoginPage.tsx | 67 ++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 5 deletions(-) diff --git a/v2/frontend/src/pages/LoginPage.tsx b/v2/frontend/src/pages/LoginPage.tsx index be0fb79..8af7dfe 100644 --- a/v2/frontend/src/pages/LoginPage.tsx +++ b/v2/frontend/src/pages/LoginPage.tsx @@ -1,6 +1,63 @@ +import React, { useState } from "react"; +import styled from "styled-components"; +import LoginInputField from "../components/LoginInputField"; + const LoginPage = () => { - return
LoginPage
; - }; - - export default LoginPage; - \ No newline at end of file + const [id, setId] = useState(""); + const [pw, setPw] = useState(""); + + return ( + + CABI-Onboarding + setId(e.target.value)} + placeholder="id" + /> + setPw(e.target.value)} + placeholder="pw" + /> + 회원가입 + 로그인 + + ); +}; + +const LoginPageStyled = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + height: 100vh; + background-color: #f9f9f9; +`; + +const LoginTitleStyled = styled.div` + font-size: 2rem; + font-weight: 700; + line-height: 3rem; + margin-bottom: 2rem; +`; + +const RegisterStyled = styled.div` + font-size: 1rem; + color: #9747ff; + line-height: 2rem; + margin-top: 1rem; +`; + +const LoginButtonStyled = styled.button` + padding: 0.5rem 1rem; + background-color: #9747ff; + color: #ffffff; + font-size: 1rem; + border: none; + border-radius: 4px; + cursor: pointer; + margin-top: 1rem; +`; + +export default LoginPage; From 82f1d8d21caa6a7332d4ecf4482cf86e6e0b4a7e Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Tue, 21 Jan 2025 20:42:37 +0900 Subject: [PATCH 012/104] =?UTF-8?q?[FE]=20FEAT:=20RegisterPage=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/pages/RegisterPage.tsx | 67 ++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 5 deletions(-) diff --git a/v2/frontend/src/pages/RegisterPage.tsx b/v2/frontend/src/pages/RegisterPage.tsx index 4741d19..af78f68 100644 --- a/v2/frontend/src/pages/RegisterPage.tsx +++ b/v2/frontend/src/pages/RegisterPage.tsx @@ -1,6 +1,63 @@ +import React, { useState } from "react"; +import styled from "styled-components"; +import LoginInputField from "../components/LoginInputField"; + const RegisterPage = () => { - return
RegisterPage
; - }; - - export default RegisterPage; - \ No newline at end of file + const [id, setId] = useState(""); + const [pw, setPw] = useState(""); + + return ( + + CABI-Onboarding + setId(e.target.value)} + placeholder="id" + /> + setPw(e.target.value)} + placeholder="pw" + /> + 비밀번호는 변경할 수 없습니다 + 회원가입 + + ); +}; + +const RegisterPageStyled = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + height: 100vh; + background-color: #f9f9f9; +`; + +const RegisterTitleStyled = styled.div` + font-size: 2rem; + font-weight: 700; + line-height: 3rem; + margin-bottom: 2rem; +`; + +const RegisterStyled = styled.div` + font-size: 1rem; + color: #9747ff; + line-height: 2rem; + margin-top: 1rem; +`; + +const LoginButtonStyled = styled.button` + padding: 0.5rem 1rem; + background-color: #9747ff; + color: #ffffff; + font-size: 1rem; + border: none; + border-radius: 4px; + cursor: pointer; + margin-top: 1rem; +`; + +export default RegisterPage; From 5159d3db12325e77756c6df53db5c944903938dc Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Tue, 21 Jan 2025 20:43:08 +0900 Subject: [PATCH 013/104] =?UTF-8?q?[FE]=20CHORE:=20SendInputField=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EC=97=90=20=EC=9E=84=EC=9D=98=EB=A1=9C=20{}?= =?UTF-8?q?=20export?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/components/SendInputField.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/v2/frontend/src/components/SendInputField.tsx b/v2/frontend/src/components/SendInputField.tsx index e69de29..cb0ff5c 100644 --- a/v2/frontend/src/components/SendInputField.tsx +++ b/v2/frontend/src/components/SendInputField.tsx @@ -0,0 +1 @@ +export {}; From 9c343a61ec2a0d285553d522d1843f192dbed09a Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Tue, 21 Jan 2025 20:56:02 +0900 Subject: [PATCH 014/104] =?UTF-8?q?[FE]=20FEAT:=20LoginInputField=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=EC=9D=98=20width=EB=A5=BC?= =?UTF-8?q?=20350px=EB=A1=9C=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/components/LoginInputField.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/frontend/src/components/LoginInputField.tsx b/v2/frontend/src/components/LoginInputField.tsx index 683c961..b66fd9f 100644 --- a/v2/frontend/src/components/LoginInputField.tsx +++ b/v2/frontend/src/components/LoginInputField.tsx @@ -37,7 +37,7 @@ const LoginInputFieldWrapper = styled.div` `; const LoginInputFieldStyled = styled.input<{ $isFocus: boolean }>` - width: 100%; + width: 350px; padding: 0.5rem 1rem; font-size: 1rem; border: 2px solid ${({ $isFocus }) => ($isFocus ? "#9747ff" : "#D7D7D7")}; From 4757c19a2eaccf245407f81cf280fd0b70d28194 Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Tue, 21 Jan 2025 21:08:10 +0900 Subject: [PATCH 015/104] =?UTF-8?q?[FE]=20REMOVE:=20LoginInputField.tsx=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/LoginInputField.tsx | 53 ------------------- 1 file changed, 53 deletions(-) delete mode 100644 v2/frontend/src/components/LoginInputField.tsx diff --git a/v2/frontend/src/components/LoginInputField.tsx b/v2/frontend/src/components/LoginInputField.tsx deleted file mode 100644 index b66fd9f..0000000 --- a/v2/frontend/src/components/LoginInputField.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import React, { useState } from "react"; -import styled from "styled-components"; - -type LoginInputFieldProps = { - value: string; - onChange: (e: React.ChangeEvent) => void; - placeholder?: string; -}; - -const LoginInputField: React.FC = ({ - value, - onChange, - placeholder, -}) => { - const [isFocused, setIsFocused] = useState(false); - - return ( - - setIsFocused(true)} - onBlur={() => setIsFocused(false)} - /> - - ); -}; - -const LoginInputFieldWrapper = styled.div` - display: flex; - justify-content: center; - align-items: center; - width: 100%; - margin-bottom: 1rem; -`; - -const LoginInputFieldStyled = styled.input<{ $isFocus: boolean }>` - width: 350px; - padding: 0.5rem 1rem; - font-size: 1rem; - border: 2px solid ${({ $isFocus }) => ($isFocus ? "#9747ff" : "#D7D7D7")}; - border-radius: 4px; - outline: none; - transition: border-color 0.3s; - - &::placeholder { - color: #858486; - } -`; - -export default LoginInputField; From bf25e07da23ee4fe7bdc29208db3cb6e6a21061f Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Tue, 21 Jan 2025 21:08:46 +0900 Subject: [PATCH 016/104] =?UTF-8?q?[FE]=20FIX:=20LoginInputField=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=A5=BC=20UserInputField?= =?UTF-8?q?=20=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/components/UserInputField.tsx | 53 +++++++++++++++++++ v2/frontend/src/pages/LoginPage.tsx | 15 +++--- v2/frontend/src/pages/RegisterPage.tsx | 21 ++++---- 3 files changed, 72 insertions(+), 17 deletions(-) create mode 100644 v2/frontend/src/components/UserInputField.tsx diff --git a/v2/frontend/src/components/UserInputField.tsx b/v2/frontend/src/components/UserInputField.tsx new file mode 100644 index 0000000..b66fd9f --- /dev/null +++ b/v2/frontend/src/components/UserInputField.tsx @@ -0,0 +1,53 @@ +import React, { useState } from "react"; +import styled from "styled-components"; + +type LoginInputFieldProps = { + value: string; + onChange: (e: React.ChangeEvent) => void; + placeholder?: string; +}; + +const LoginInputField: React.FC = ({ + value, + onChange, + placeholder, +}) => { + const [isFocused, setIsFocused] = useState(false); + + return ( + + setIsFocused(true)} + onBlur={() => setIsFocused(false)} + /> + + ); +}; + +const LoginInputFieldWrapper = styled.div` + display: flex; + justify-content: center; + align-items: center; + width: 100%; + margin-bottom: 1rem; +`; + +const LoginInputFieldStyled = styled.input<{ $isFocus: boolean }>` + width: 350px; + padding: 0.5rem 1rem; + font-size: 1rem; + border: 2px solid ${({ $isFocus }) => ($isFocus ? "#9747ff" : "#D7D7D7")}; + border-radius: 4px; + outline: none; + transition: border-color 0.3s; + + &::placeholder { + color: #858486; + } +`; + +export default LoginInputField; diff --git a/v2/frontend/src/pages/LoginPage.tsx b/v2/frontend/src/pages/LoginPage.tsx index 8af7dfe..5a5311b 100644 --- a/v2/frontend/src/pages/LoginPage.tsx +++ b/v2/frontend/src/pages/LoginPage.tsx @@ -1,6 +1,6 @@ -import React, { useState } from "react"; +import { useState } from "react"; import styled from "styled-components"; -import LoginInputField from "../components/LoginInputField"; +import UserInputField from "../components/UserInputField"; const LoginPage = () => { const [id, setId] = useState(""); @@ -9,17 +9,17 @@ const LoginPage = () => { return ( CABI-Onboarding - setId(e.target.value)} placeholder="id" /> - setPw(e.target.value)} placeholder="pw" /> - 회원가입 + 회원가입 로그인 ); @@ -42,7 +42,7 @@ const LoginTitleStyled = styled.div` margin-bottom: 2rem; `; -const RegisterStyled = styled.div` +const RegisterNavStyled = styled.div` font-size: 1rem; color: #9747ff; line-height: 2rem; @@ -50,7 +50,8 @@ const RegisterStyled = styled.div` `; const LoginButtonStyled = styled.button` - padding: 0.5rem 1rem; + width: 350px; + padding: 0.8rem 1rem; background-color: #9747ff; color: #ffffff; font-size: 1rem; diff --git a/v2/frontend/src/pages/RegisterPage.tsx b/v2/frontend/src/pages/RegisterPage.tsx index af78f68..11893c1 100644 --- a/v2/frontend/src/pages/RegisterPage.tsx +++ b/v2/frontend/src/pages/RegisterPage.tsx @@ -1,6 +1,6 @@ -import React, { useState } from "react"; +import { useState } from "react"; import styled from "styled-components"; -import LoginInputField from "../components/LoginInputField"; +import UserInputField from "../components/UserInputField"; const RegisterPage = () => { const [id, setId] = useState(""); @@ -9,18 +9,18 @@ const RegisterPage = () => { return ( CABI-Onboarding - setId(e.target.value)} placeholder="id" /> - setPw(e.target.value)} placeholder="pw" /> - 비밀번호는 변경할 수 없습니다 - 회원가입 + 비밀번호는 변경할 수 없습니다 + 회원가입 ); }; @@ -42,15 +42,16 @@ const RegisterTitleStyled = styled.div` margin-bottom: 2rem; `; -const RegisterStyled = styled.div` +const RegisterTextStyled = styled.div` font-size: 1rem; - color: #9747ff; + color: #858486; line-height: 2rem; margin-top: 1rem; `; -const LoginButtonStyled = styled.button` - padding: 0.5rem 1rem; +const RegisterButtonStyled = styled.button` + width: 350px; + padding: 0.8rem 1rem; background-color: #9747ff; color: #ffffff; font-size: 1rem; From 98de1620f1eb363882afa59c7d771b9b12ebb09c Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Tue, 21 Jan 2025 21:23:15 +0900 Subject: [PATCH 017/104] =?UTF-8?q?[FE]=20FEAT:=20Link=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=ED=81=B4=EB=A6=AD=EC=8B=9C=20Reg?= =?UTF-8?q?ister=20=ED=8E=98=EC=9D=B4=EC=A7=80=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=ED=95=98=EB=8F=84=EB=A1=9D=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/pages/LoginPage.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/v2/frontend/src/pages/LoginPage.tsx b/v2/frontend/src/pages/LoginPage.tsx index 5a5311b..94f32cd 100644 --- a/v2/frontend/src/pages/LoginPage.tsx +++ b/v2/frontend/src/pages/LoginPage.tsx @@ -1,4 +1,5 @@ import { useState } from "react"; +import { Link } from "react-router-dom"; import styled from "styled-components"; import UserInputField from "../components/UserInputField"; @@ -19,7 +20,7 @@ const LoginPage = () => { onChange={(e) => setPw(e.target.value)} placeholder="pw" /> - 회원가입 + 회원가입 로그인 ); @@ -42,7 +43,7 @@ const LoginTitleStyled = styled.div` margin-bottom: 2rem; `; -const RegisterNavStyled = styled.div` +const LinkStyled = styled(Link)` font-size: 1rem; color: #9747ff; line-height: 2rem; From 68f5949c65ab1477acd0d896599cde52b22560bb Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Tue, 21 Jan 2025 21:32:50 +0900 Subject: [PATCH 018/104] =?UTF-8?q?[FE]=20FEAT:=20input=20=ED=95=84?= =?UTF-8?q?=ED=8A=B8=EC=99=80=20=EB=B2=84=ED=8A=BC=20css=20=EC=86=8D?= =?UTF-8?q?=EC=84=B1=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/components/UserInputField.tsx | 1 + v2/frontend/src/pages/LoginPage.tsx | 1 + v2/frontend/src/pages/RegisterPage.tsx | 1 + 3 files changed, 3 insertions(+) diff --git a/v2/frontend/src/components/UserInputField.tsx b/v2/frontend/src/components/UserInputField.tsx index b66fd9f..13ae164 100644 --- a/v2/frontend/src/components/UserInputField.tsx +++ b/v2/frontend/src/components/UserInputField.tsx @@ -38,6 +38,7 @@ const LoginInputFieldWrapper = styled.div` const LoginInputFieldStyled = styled.input<{ $isFocus: boolean }>` width: 350px; + height: 34px; padding: 0.5rem 1rem; font-size: 1rem; border: 2px solid ${({ $isFocus }) => ($isFocus ? "#9747ff" : "#D7D7D7")}; diff --git a/v2/frontend/src/pages/LoginPage.tsx b/v2/frontend/src/pages/LoginPage.tsx index 94f32cd..c3fba8e 100644 --- a/v2/frontend/src/pages/LoginPage.tsx +++ b/v2/frontend/src/pages/LoginPage.tsx @@ -52,6 +52,7 @@ const LinkStyled = styled(Link)` const LoginButtonStyled = styled.button` width: 350px; + height: 56px; padding: 0.8rem 1rem; background-color: #9747ff; color: #ffffff; diff --git a/v2/frontend/src/pages/RegisterPage.tsx b/v2/frontend/src/pages/RegisterPage.tsx index 11893c1..f774080 100644 --- a/v2/frontend/src/pages/RegisterPage.tsx +++ b/v2/frontend/src/pages/RegisterPage.tsx @@ -51,6 +51,7 @@ const RegisterTextStyled = styled.div` const RegisterButtonStyled = styled.button` width: 350px; + height: 56px; padding: 0.8rem 1rem; background-color: #9747ff; color: #ffffff; From 7f9fdc41e801e03c38fce6c64d72c6823f4dd852 Mon Sep 17 00:00:00 2001 From: jiminChoi Date: Tue, 21 Jan 2025 21:44:55 +0900 Subject: [PATCH 019/104] =?UTF-8?q?[FE]=20FEAT:=20sendPage=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/pages/SendPage.tsx | 66 ++++++++++++++---------------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/v2/frontend/src/pages/SendPage.tsx b/v2/frontend/src/pages/SendPage.tsx index b3a7fac..8dec084 100644 --- a/v2/frontend/src/pages/SendPage.tsx +++ b/v2/frontend/src/pages/SendPage.tsx @@ -3,15 +3,12 @@ import styled from "styled-components"; const SendPage = () => { return ( - -

알림

-
+ 알림 - 알림 보내기 - 받는이(Intra ID/ Channel)* + 받는이 ( Intra ID / @everyone ) * {/* { - 메시지 내용* + 메시지 내용 * + + + + + 사진 ( jpg, jpeg, png ) @@ -35,9 +37,11 @@ const SendPage = () => { export default SendPage; const WrapperStyled = styled.div` + /* height: 100%; */ + height: 500px; display: flex; flex-direction: column; - justify-content: center; + justify-content: space-between; align-items: center; padding: 60px 0; `; @@ -46,17 +50,14 @@ const TitleContainerStyled = styled.div` width: 80%; max-width: 1000px; display: flex; - justify-content: space-between; + justify-content: center; align-items: center; border-bottom: 2px solid var(--service-man-title-border-btm-color); margin-bottom: 70px; font-weight: 700; - - .title { - font-size: 1.25rem; - letter-spacing: -0.02rem; - margin-bottom: 20px; - } + font-size: 1.25rem; + letter-spacing: -0.02rem; + margin-bottom: 20px; `; const ContainerStyled = styled.div` @@ -87,11 +88,10 @@ const CapsuleButtonStyled = styled.span<{ justify-content: center; align-items: center; padding: 8px 20px; - background: var(--card-bg-color); - border: 1px solid var(--capsule-btn-border-color); + background: #f5f5f5; + border: 1px solid #ffffff; border-radius: 22px; cursor: pointer; - `; const FormWappingStyled = styled.div` @@ -99,7 +99,7 @@ const FormWappingStyled = styled.div` flex-direction: column; justify-content: flex-start; align-items: center; - background-color: var(--card-bg-color); + background-color: #f5f5f5; border-radius: 10px; padding: 30px 20px; gap: 20px; @@ -110,10 +110,10 @@ const FormContainerStyled = styled.div` `; const FormSubTitleStyled = styled.h3` font-size: 0.875rem; - color: var(--gray-line-btn-color); + color: #7b7b7b; margin-bottom: 10px; - span { - color: var(--expired-color); + .red { + color: #ff4e4e; } `; @@ -122,13 +122,13 @@ const FormTextareaStyled = styled.textarea` box-sizing: border-box; width: 100%; min-height: 200px; - background-color: var(--card-content-bg-color); + background-color: #3d3f40; border-radius: 8px; - border: 1px solid var(--capsule-btn-border-color); + border: 1px solid #ffffff; resize: none; outline: none; :focus { - border: 1px solid var(--sys-main-color); + border: 1px solid #9747ff; } text-align: left; padding: 10px; @@ -140,25 +140,21 @@ const FormTextareaStyled = styled.textarea` const FormButtonContainerStyled = styled.div` width: 100%; display: flex; - justify-content: space-between; + justify-content: flex-end; `; -const FormButtonStyled = styled.button<{ primary?: boolean }>` +const FormButtonStyled = styled.button` width: auto; height: auto; padding: 10px 16px; font-size: 0.875rem; - background-color: ${(props) => - props.primary ? "var(--sys-main-color)" : "var(--card-content-bg-color)"}; - color: ${(props) => - props.primary - ? "var(--white-text-with-bg-color)" - : "var(--normal-text-color)"}; + background-color: #9747ff; + color: #ffffff; font-weight: 700; - border: 1px solid var(--capsule-btn-border-color); + border: 1px solid #ffffff; border-radius: 4px; cursor: pointer; - :hover { + /* :hover { opacity: 0.85; - } + } */ `; From 1243c1bd791787ee78b42109b0dab8302a8acc6a Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Tue, 21 Jan 2025 22:57:19 +0900 Subject: [PATCH 020/104] =?UTF-8?q?[FE]=20FEAT:=20LoginPage,=20RegisterPag?= =?UTF-8?q?e=20=EC=97=90=20=ED=8F=B0=ED=8A=B8=20=EC=86=8D=EC=84=B1=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/pages/LoginPage.tsx | 1 + v2/frontend/src/pages/RegisterPage.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/v2/frontend/src/pages/LoginPage.tsx b/v2/frontend/src/pages/LoginPage.tsx index c3fba8e..3cf851c 100644 --- a/v2/frontend/src/pages/LoginPage.tsx +++ b/v2/frontend/src/pages/LoginPage.tsx @@ -27,6 +27,7 @@ const LoginPage = () => { }; const LoginPageStyled = styled.div` + font-family: "Noto Sans KR", sans-serif; display: flex; flex-direction: column; justify-content: center; diff --git a/v2/frontend/src/pages/RegisterPage.tsx b/v2/frontend/src/pages/RegisterPage.tsx index f774080..cc01838 100644 --- a/v2/frontend/src/pages/RegisterPage.tsx +++ b/v2/frontend/src/pages/RegisterPage.tsx @@ -26,6 +26,7 @@ const RegisterPage = () => { }; const RegisterPageStyled = styled.div` + font-family: "Noto Sans KR", sans-serif; display: flex; flex-direction: column; justify-content: center; From a194aac36740e893145faab512b135b34e4ac973 Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Tue, 21 Jan 2025 23:36:59 +0900 Subject: [PATCH 021/104] =?UTF-8?q?[FE]=20FEAT:=20custom.d.ts=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/custom.d.ts | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 v2/frontend/src/custom.d.ts diff --git a/v2/frontend/src/custom.d.ts b/v2/frontend/src/custom.d.ts new file mode 100644 index 0000000..b1d9a52 --- /dev/null +++ b/v2/frontend/src/custom.d.ts @@ -0,0 +1,7 @@ +declare module "*.svg" { + import * as React from "react"; + + export const ReactComponent: React.FunctionComponent< + React.SVGProps & { title?: string } + >; +} From c0370b5200895f4be155448ca9c3fe17e386a7ea Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Tue, 21 Jan 2025 23:37:16 +0900 Subject: [PATCH 022/104] =?UTF-8?q?[FE]=20FEAT:=20newYear.svg=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/assets/images/newYear.svg | 1 + 1 file changed, 1 insertion(+) create mode 100644 v2/frontend/src/assets/images/newYear.svg diff --git a/v2/frontend/src/assets/images/newYear.svg b/v2/frontend/src/assets/images/newYear.svg new file mode 100644 index 0000000..0b412ad --- /dev/null +++ b/v2/frontend/src/assets/images/newYear.svg @@ -0,0 +1 @@ + From 6c9322ca7d491fa93b72c658b02d429fd53f5d08 Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Tue, 21 Jan 2025 23:38:46 +0900 Subject: [PATCH 023/104] =?UTF-8?q?[FE]=20FEAT:=20newYear.svg=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/assets/images/newYear.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/frontend/src/assets/images/newYear.svg b/v2/frontend/src/assets/images/newYear.svg index 0b412ad..2748a8e 100644 --- a/v2/frontend/src/assets/images/newYear.svg +++ b/v2/frontend/src/assets/images/newYear.svg @@ -1 +1 @@ - + From e129b2766cd6db9d15addaa52945ff9c2ce027e3 Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Tue, 21 Jan 2025 23:39:12 +0900 Subject: [PATCH 024/104] =?UTF-8?q?[FE]=20FEAT:=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=EC=97=90=20NewYearImg?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/pages/LoginPage.tsx | 39 +++++++++++++++++++---------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/v2/frontend/src/pages/LoginPage.tsx b/v2/frontend/src/pages/LoginPage.tsx index 3cf851c..c9ffcf9 100644 --- a/v2/frontend/src/pages/LoginPage.tsx +++ b/v2/frontend/src/pages/LoginPage.tsx @@ -2,6 +2,7 @@ import { useState } from "react"; import { Link } from "react-router-dom"; import styled from "styled-components"; import UserInputField from "../components/UserInputField"; +import { ReactComponent as NewYearImg } from "../assets/images/newYear.svg"; const LoginPage = () => { const [id, setId] = useState(""); @@ -9,19 +10,22 @@ const LoginPage = () => { return ( - CABI-Onboarding - setId(e.target.value)} - placeholder="id" - /> - setPw(e.target.value)} - placeholder="pw" - /> - 회원가입 - 로그인 + + + CABI-Onboarding + setId(e.target.value)} + placeholder="id" + /> + setPw(e.target.value)} + placeholder="pw" + /> + 회원가입 + 로그인 + ); }; @@ -37,10 +41,19 @@ const LoginPageStyled = styled.div` background-color: #f9f9f9; `; +const LoginSectionStyled = styled.div` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; +`; + const LoginTitleStyled = styled.div` font-size: 2rem; font-weight: 700; line-height: 3rem; + margin-top: 2rem; margin-bottom: 2rem; `; From 6e47385f00e04a6d1ab9a0321062c5bea75a6027 Mon Sep 17 00:00:00 2001 From: JunSeong Date: Tue, 21 Jan 2025 23:49:21 +0900 Subject: [PATCH 025/104] =?UTF-8?q?[FE]=20CHORE:=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20import=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/App.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/v2/frontend/src/App.tsx b/v2/frontend/src/App.tsx index b66a60b..0ebb6e2 100644 --- a/v2/frontend/src/App.tsx +++ b/v2/frontend/src/App.tsx @@ -1,11 +1,4 @@ -import { - RouterProvider, - createBrowserRouter, -} from "react-router-dom"; -import SendPage from "./pages/SendPage"; -import LoginPage from "./pages/LoginPage"; -import RegisterPage from "./pages/RegisterPage"; -import ListPage from "./pages/ListPage"; +import { RouterProvider, createBrowserRouter } from "react-router-dom"; import { routes } from "./routes"; const router = createBrowserRouter(routes); From 3eeef4eb7fd2c24169ff333ff30165bf7ae5413b Mon Sep 17 00:00:00 2001 From: JunSeong Date: Tue, 21 Jan 2025 23:50:08 +0900 Subject: [PATCH 026/104] =?UTF-8?q?[FE]=20FEAT:=20=EB=8D=95=EB=8B=B4=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EB=B6=88=EB=9F=AC=EC=98=A4?= =?UTF-8?q?=EA=B8=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/pages/ListPage.tsx | 55 +++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 4 deletions(-) diff --git a/v2/frontend/src/pages/ListPage.tsx b/v2/frontend/src/pages/ListPage.tsx index 7dd9991..4dd7d97 100644 --- a/v2/frontend/src/pages/ListPage.tsx +++ b/v2/frontend/src/pages/ListPage.tsx @@ -1,6 +1,53 @@ +import { useEffect, useState } from "react"; +import { Link } from "react-router-dom"; +import { Filter, LIST_SIZE } from "../constant"; +import { getMessages } from "../api/messages"; + const ListPage = () => { - return
ListPage
; + const [items, setItems] = useState([]); + const [page, setPage] = useState(0); + const [isLoading, setIsLoading] = useState(false); + const [category, setCategory] = useState(Filter.TO_EVERYONE); + + const fetchItems = async () => { + try { + setIsLoading(true); + const res = await getMessages({ page, size: LIST_SIZE, category }); + setItems((items) => [...items, ...res.data.messages]); + setPage((page) => page + 1); + setIsLoading(false); + } catch (error) {} }; - - export default ListPage; - \ No newline at end of file + + useEffect(() => { + fetchItems(); + }, []); + + return ( + <> + 덕담 보내러 가기 +

덕담 보기

+
+ + + +
+
+
리스트
+ {items.map((item: any, index) => ( +
+
{item.senderName}
+
{item.receiverName}
+
{item.context}
+
{item.imageUrl}
+
+ ))} +
+
{isLoading ? `Loading...` : `더보기`}
+ + ); +}; + +export default ListPage; From 1c5492e2c995df6863669e41f65cec707cfe5b3b Mon Sep 17 00:00:00 2001 From: Jihyun Kim Date: Wed, 22 Jan 2025 00:11:32 +0900 Subject: [PATCH 027/104] =?UTF-8?q?[FE]=20FIX:=20reset-css=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20=EC=A0=84=EC=97=AD=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/App.tsx | 10 ++-------- v2/frontend/src/index.css | 35 +++++------------------------------ 2 files changed, 7 insertions(+), 38 deletions(-) diff --git a/v2/frontend/src/App.tsx b/v2/frontend/src/App.tsx index b66a60b..67808b3 100644 --- a/v2/frontend/src/App.tsx +++ b/v2/frontend/src/App.tsx @@ -1,12 +1,6 @@ -import { - RouterProvider, - createBrowserRouter, -} from "react-router-dom"; -import SendPage from "./pages/SendPage"; -import LoginPage from "./pages/LoginPage"; -import RegisterPage from "./pages/RegisterPage"; -import ListPage from "./pages/ListPage"; +import { RouterProvider, createBrowserRouter } from "react-router-dom"; import { routes } from "./routes"; +import "./index.css"; const router = createBrowserRouter(routes); diff --git a/v2/frontend/src/index.css b/v2/frontend/src/index.css index 60286cf..1f7192f 100644 --- a/v2/frontend/src/index.css +++ b/v2/frontend/src/index.css @@ -68,36 +68,6 @@ --ref-black-shadow-300: rgba(0, 0, 0, 0.4); --ref-black-shadow-400: rgba(0, 0, 0, 0.8); - /* cabinet */ - --full-color: var(--ref-gray-200); - --banned-color: var(--ref-gray-700); - --broken-color: var(--ref-gray-700); - --session-color: var(--ref-gray-200); - - /* custom color */ - --custom-blue-100: var(--ref-blue-300); - --custom-blue-200: var(--ref-blue-500); - --custom-blue-300: var(--ref-blue-600); - --custom-pink-100: var(--ref-pink-100); - --custom-pink-200: var(--ref-pink-200); - --custom-pink-300: var(--ref-pink-300); - --custom-purple-100: var(--ref-purple-300); - --custom-purple-200: var(--ref-purple-500); - --custom-orange: var(--ref-orange-100); - --custom-yellow: var(--ref-yellow); - --custom-green-100: var(--ref-green-300); - --custom-green-200: var(--ref-green-400); - - /* component variable */ - --sys-sub-color: var(--ref-purple-400); - --sys-default-sub-color: var(--ref-purple-400); - --sys-presentation-sub-color: var(--ref-blue-300); - --tooltip-shadow-color: var(--ref-black-shadow-400); - --color-picker-border-shadow-color: var(--ref-black-shadow-200); - --presentation-detail-available-color: var(--ref-blue-200); - --presentation-blue-pagination-btn-color: var(--ref-blue-500); - --mine-text-color: var(--ref-black); - /* font variable */ --main-font: "Noto Sans KR", sans-serif; --building-font: "Do Hyeon", sans-serif; @@ -195,6 +165,11 @@ input { box-sizing: border-box; } +textarea { + outline: none; + box-sizing: border-box; +} + .modal { border-radius: 10px; box-shadow: 10px 10px 40px 0px var(--login-card-border-shadow-color); From de245403a2a2f9d6120ec927706c37f480e58374 Mon Sep 17 00:00:00 2001 From: Jihyun Kim Date: Wed, 22 Jan 2025 00:12:45 +0900 Subject: [PATCH 028/104] =?UTF-8?q?[FE]=20FEAT:=20send=20page=EC=9D=98=20i?= =?UTF-8?q?nput,=20textarea=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/SearchInputField.tsx | 41 +++++++++++++++++++ v2/frontend/src/components/SendInputField.tsx | 1 - v2/frontend/src/pages/SendPage.tsx | 35 ++++++++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 v2/frontend/src/components/SearchInputField.tsx delete mode 100644 v2/frontend/src/components/SendInputField.tsx diff --git a/v2/frontend/src/components/SearchInputField.tsx b/v2/frontend/src/components/SearchInputField.tsx new file mode 100644 index 0000000..b596d18 --- /dev/null +++ b/v2/frontend/src/components/SearchInputField.tsx @@ -0,0 +1,41 @@ +import { useState } from "react"; +import styled from "styled-components"; + +const SearchInputField = ({ + placeHolder, + inputText, +}: { + placeHolder: string; + inputText: React.RefObject; +}) => { + const [isFocused, setIsFocused] = useState(false); + + return ( + setIsFocused(true)} + onBlur={() => setIsFocused(false)} + // onChange={() => debounce("SlackAlarmSearch", typeSearchInput, 300)} + // onKeyDown={handleInputKey} + /> + ); +}; + +const SearchInputFieldStyled = styled.input<{ $isFocus: boolean }>` + width: 100%; + height: 40px; + background-color: var(--ref-white); + border-radius: 8px; + border: 2px solid + ${({ $isFocus }) => ($isFocus ? "var(--ref-purple-500)" : "transparent")}; + text-align: left; + padding: 0 10px; + ::placeholder { + color: var(--ref-gray-400); + } +`; + +export default SearchInputField; diff --git a/v2/frontend/src/components/SendInputField.tsx b/v2/frontend/src/components/SendInputField.tsx deleted file mode 100644 index cb0ff5c..0000000 --- a/v2/frontend/src/components/SendInputField.tsx +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/v2/frontend/src/pages/SendPage.tsx b/v2/frontend/src/pages/SendPage.tsx index 8dec084..c6866fc 100644 --- a/v2/frontend/src/pages/SendPage.tsx +++ b/v2/frontend/src/pages/SendPage.tsx @@ -1,6 +1,12 @@ import styled from "styled-components"; +import { useRef, useState } from "react"; +import SearchInputField from "../components/SearchInputField"; const SendPage = () => { + const idSearchInputRef = useRef(null); + const messageTextAreaRef = useRef(null); + const [isFocused, setIsFocused] = useState(false); + return ( 알림 @@ -10,6 +16,10 @@ const SendPage = () => { 받는이 ( Intra ID / @everyone ) * + {/* { 메시지 내용 * + setIsFocused(true)} + onBlur={() => setIsFocused(false)} + /> + {/* textarea length */} @@ -37,6 +55,7 @@ const SendPage = () => { export default SendPage; const WrapperStyled = styled.div` + font-family: "Noto Sans KR", sans-serif; /* height: 100%; */ height: 500px; display: flex; @@ -117,6 +136,22 @@ const FormSubTitleStyled = styled.h3` } `; +const SendTextFieldStyled = styled.textarea<{ $isFocus: boolean }>` + /* TODO: font 및 텍스트 크기 조정(통일) */ + /* font-family: "Noto Sans KR", sans-serif; */ + width: 100%; + height: 40px; + background-color: var(--ref-white); + border-radius: 8px; + border: 2px solid + ${({ $isFocus }) => ($isFocus ? "var(--ref-purple-500)" : "transparent")}; + text-align: left; + padding: 10px; + ::placeholder { + color: var(--ref-gray-400); + } +`; + const FormTextareaStyled = styled.textarea` color: var(--normal-text-color); box-sizing: border-box; From aa570b6e045c994878675ffa8598da3fd721d1c4 Mon Sep 17 00:00:00 2001 From: JunSeong Date: Wed, 22 Jan 2025 00:59:39 +0900 Subject: [PATCH 029/104] =?UTF-8?q?[FE]=20FEAT:=20constant=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/constant.ts | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 v2/frontend/src/constant.ts diff --git a/v2/frontend/src/constant.ts b/v2/frontend/src/constant.ts new file mode 100644 index 0000000..c5ed920 --- /dev/null +++ b/v2/frontend/src/constant.ts @@ -0,0 +1,7 @@ +export enum Filter { + TO_EVERYONE = 0, + TO_ME = 1, + FROM_ME = 2, +} + +export const LIST_SIZE = 5; From 87d90146d1454fbdadd8d4d9f0465ec48394efa5 Mon Sep 17 00:00:00 2001 From: lamodadite Date: Wed, 22 Jan 2025 15:17:30 +0900 Subject: [PATCH 030/104] =?UTF-8?q?FIX=20:=20RDS,=20S3=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0=20=EB=90=98=EB=8F=84=EB=A1=9D=20=ED=99=98=EA=B2=BD=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .env 파일 인식 안되는 문제로 인해 propertise 파일로 인식하도록 설정 변경 --- v2/backend/greetingCard/.gitignore | 3 ++- v2/backend/greetingCard/build.gradle | 2 +- .../java/com/cabi/greetingCard/configuration/AWSConfig.java | 2 +- v2/backend/greetingCard/src/main/resources/application.yml | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/v2/backend/greetingCard/.gitignore b/v2/backend/greetingCard/.gitignore index 3d5431b..c8d5658 100644 --- a/v2/backend/greetingCard/.gitignore +++ b/v2/backend/greetingCard/.gitignore @@ -36,4 +36,5 @@ out/ ### VS Code ### .vscode/ -.env \ No newline at end of file +.env +v2/backend/greetingCard/src/main/resources/application.properties \ No newline at end of file diff --git a/v2/backend/greetingCard/build.gradle b/v2/backend/greetingCard/build.gradle index 2276fee..28b32c0 100644 --- a/v2/backend/greetingCard/build.gradle +++ b/v2/backend/greetingCard/build.gradle @@ -10,7 +10,7 @@ sourceCompatibility = '17' java { toolchain { - languageVersion = JavaLanguageVersion.of(21) + languageVersion = JavaLanguageVersion.of(17) } } diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/configuration/AWSConfig.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/configuration/AWSConfig.java index 7d6d4a4..abf72ee 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/configuration/AWSConfig.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/configuration/AWSConfig.java @@ -12,7 +12,7 @@ /** * 해당 파일은 수정하지 않아도 됩니다. 따봉 6기가 config를 작성해뒀다구! *

- * 동작하지 않는다면 에러 로그를 확인하고 .env 파일을 제대로 넣었는지 확인해보세요!! + * 동작하지 않는다면 에러 로그를 확인하고 application.properties 파일을 제대로 넣었는지 확인해보세요!! */ @Configuration public class AWSConfig { diff --git a/v2/backend/greetingCard/src/main/resources/application.yml b/v2/backend/greetingCard/src/main/resources/application.yml index 7cd1a3c..39772a8 100644 --- a/v2/backend/greetingCard/src/main/resources/application.yml +++ b/v2/backend/greetingCard/src/main/resources/application.yml @@ -12,7 +12,7 @@ spring: max-file-size: 30MB max-request-size: 30MB config: - import: optional:file:.env[.properties] + import: application.properties cloud: aws: From 299c21d04765f326e16d22ff3a8eb42d30310633 Mon Sep 17 00:00:00 2001 From: lamodadite Date: Wed, 22 Jan 2025 15:35:52 +0900 Subject: [PATCH 031/104] =?UTF-8?q?TEST=20:=20H2=20test=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=B4=20=ED=85=8C=EC=9D=B4=EB=B8=94=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20User=20->=20Users=EB=A1=9C=20=EB=B3=80=EA=B2=BD=20(?= =?UTF-8?q?User=EA=B0=80=20h2=EC=9D=98=20=EC=98=88=EC=95=BD=EC=96=B4?= =?UTF-8?q?=EB=9D=BC=EC=84=9C=20=ED=85=8C=EC=9D=B4=EB=B8=94=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=9D=B4=20=EC=95=88=EB=90=A8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/cabi/greetingCard/user/domain/User.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/domain/User.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/domain/User.java index 66de626..52ca297 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/domain/User.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/domain/User.java @@ -5,6 +5,7 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.Table; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -12,6 +13,7 @@ @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter +@Table(name = "USERS") public class User { @Id From 0a7118b213fd298108299d86807fb66ef3638c77 Mon Sep 17 00:00:00 2001 From: lamodadite Date: Wed, 22 Jan 2025 15:36:05 +0900 Subject: [PATCH 032/104] =?UTF-8?q?TEST=20:=20H2=20test=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=B4=20=EC=84=A4=EC=A0=95=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/backend/greetingCard/build.gradle | 2 ++ .../src/main/resources/application.yml | 35 +++++++++++++------ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/v2/backend/greetingCard/build.gradle b/v2/backend/greetingCard/build.gradle index 28b32c0..d1d47cd 100644 --- a/v2/backend/greetingCard/build.gradle +++ b/v2/backend/greetingCard/build.gradle @@ -39,6 +39,8 @@ dependencies { implementation 'com.amazonaws:aws-java-sdk-s3:1.11.1000' implementation 'org.mariadb.jdbc:mariadb-java-client' + // 테스트용 h2 database + runtimeOnly 'com.h2database:h2' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' diff --git a/v2/backend/greetingCard/src/main/resources/application.yml b/v2/backend/greetingCard/src/main/resources/application.yml index 39772a8..230d477 100644 --- a/v2/backend/greetingCard/src/main/resources/application.yml +++ b/v2/backend/greetingCard/src/main/resources/application.yml @@ -1,18 +1,33 @@ +#spring: +# datasource: +# driver-class-name: org.mariadb.jdbc.Driver +# url: ${DB_URL} +# username: ${DB_USERNAME} +# password: ${DB_PASSWORD} +# jpa: +# hibernate: +# ddl-auto: update +# servlet: +# multipart: +# max-file-size: 30MB +# max-request-size: 30MB +# config: +# import: application.properties + spring: datasource: - driver-class-name: org.mariadb.jdbc.Driver - url: ${DB_URL} - username: ${DB_USERNAME} - password: ${DB_PASSWORD} + driver-class-name: org.h2.Driver + url: jdbc:h2:tcp://localhost/~/onboarding + username: root + password: 1234 + jpa: hibernate: ddl-auto: update - servlet: - multipart: - max-file-size: 30MB - max-request-size: 30MB - config: - import: application.properties + properties: + hibernate: + show_sql: true #콘솔에 로그가 나옴 + format_sql: true #이쁘게 해줌 cloud: aws: From c04bd57937a58772d7afe77e94be7f057ec54c81 Mon Sep 17 00:00:00 2001 From: lamodadite Date: Wed, 22 Jan 2025 16:11:00 +0900 Subject: [PATCH 033/104] =?UTF-8?q?FEAT=20:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nmae 필드의 validation 메서드로 직접 구현 @Validation 기능을 쓰고 싶었지만 커스텀 에러를 발생시킬때 복잡성이 커져서 일단 포기함 --- .../user/controller/UserController.java | 4 +-- .../user/repository/UserRepository.java | 1 + .../user/service/UserService.java | 28 +++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java index 0b113d2..afa6827 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java @@ -21,7 +21,7 @@ */ @RestController @RequiredArgsConstructor -@RequestMapping("/주체가누구지..") +@RequestMapping("/users") public class UserController { private final UserService userService; @@ -29,7 +29,7 @@ public class UserController { /** * PostMapping, GetMapping이 머지?? */ - @PostMapping("/test1") + @PostMapping("/register") public void registerUser(@RequestBody UserInfoDto userInfoDto) { userService.registerUser(userInfoDto.getName(), userInfoDto.getPassword()); } diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/repository/UserRepository.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/repository/UserRepository.java index 091b70c..c2edd8f 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/repository/UserRepository.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/repository/UserRepository.java @@ -10,4 +10,5 @@ @Repository public interface UserRepository extends JpaRepository { + boolean existsByName(String name); } diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java index a292279..1f3ac69 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java @@ -1,6 +1,7 @@ package com.cabi.greetingCard.user.service; import com.cabi.greetingCard.dto.UserSearchDto; +import com.cabi.greetingCard.exception.ExceptionStatus; import com.cabi.greetingCard.user.domain.User; import com.cabi.greetingCard.user.repository.UserRepository; import jakarta.servlet.http.Cookie; @@ -26,16 +27,43 @@ public class UserService { */ public void registerUser(String name, String password) { verifyDuplicatedName(name); + verifyNameLengthUnder10(name); + verifyNameIsNumericOrAlphabet(name); // 하나의 메서드로 따로 빼는게 나을까? 코드만 복잡해지는게 아닌지? User user = new User(name, password); userRepository.save(user); } + /** + * 유저 이름이 영어 + 숫자로만 구성되어 있는지 확인합니다. + * + * @param name + */ + private void verifyNameIsNumericOrAlphabet(String name) { + if (name == null || !name.matches("^[a-zA-Z0-9]+$")) { + throw ExceptionStatus.INVALID_NAME.asGreetingException(); + } + } + + /** + * 유저 이름의 길이가 10자 이내인지 확인합니다 + * + * @param name + */ + private void verifyNameLengthUnder10(String name) { + if (name.length() > 10) { + throw ExceptionStatus.INVALID_NAME.asGreetingException(); //TODO : 모든 exception에 에러코드 넣어야 함 + } + } + /** * 중복된 name 검증 기능 * * @param name */ public void verifyDuplicatedName(String name) { + if (userRepository.existsByName(name)) { + throw ExceptionStatus.DUPLICATED_NAME.asGreetingException(); + } } /** From 4225a8c4cb1ea607fa0f5e6399df9a82ae128460 Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Wed, 22 Jan 2025 16:42:57 +0900 Subject: [PATCH 034/104] =?UTF-8?q?[FE]=20FEAT:=20UserInputField=20?= =?UTF-8?q?=EC=9D=98=20placeholder=20=EC=A0=95=EB=A0=AC=20center=20?= =?UTF-8?q?=EC=97=90=EC=84=9C=20left=20=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/components/UserInputField.tsx | 7 ++-- v2/frontend/src/pages/LoginPage.tsx | 40 +++++++------------ v2/frontend/src/pages/RegisterPage.tsx | 5 ++- 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/v2/frontend/src/components/UserInputField.tsx b/v2/frontend/src/components/UserInputField.tsx index 13ae164..747d488 100644 --- a/v2/frontend/src/components/UserInputField.tsx +++ b/v2/frontend/src/components/UserInputField.tsx @@ -33,13 +33,14 @@ const LoginInputFieldWrapper = styled.div` justify-content: center; align-items: center; width: 100%; - margin-bottom: 1rem; + margin-bottom: 0.8rem; `; const LoginInputFieldStyled = styled.input<{ $isFocus: boolean }>` width: 350px; - height: 34px; - padding: 0.5rem 1rem; + height: 40px; + padding: 0.5rem 0.8rem; + text-align: left; font-size: 1rem; border: 2px solid ${({ $isFocus }) => ($isFocus ? "#9747ff" : "#D7D7D7")}; border-radius: 4px; diff --git a/v2/frontend/src/pages/LoginPage.tsx b/v2/frontend/src/pages/LoginPage.tsx index c9ffcf9..3cf111e 100644 --- a/v2/frontend/src/pages/LoginPage.tsx +++ b/v2/frontend/src/pages/LoginPage.tsx @@ -9,23 +9,21 @@ const LoginPage = () => { const [pw, setPw] = useState(""); return ( - + - - CABI-Onboarding - setId(e.target.value)} - placeholder="id" - /> - setPw(e.target.value)} - placeholder="pw" - /> - 회원가입 - 로그인 - + CABI-Onboarding + setId(e.target.value)} + placeholder="id" + /> + setPw(e.target.value)} + placeholder="pw" + /> + 회원가입 + 로그인 ); }; @@ -41,14 +39,6 @@ const LoginPageStyled = styled.div` background-color: #f9f9f9; `; -const LoginSectionStyled = styled.div` - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - width: 100%; -`; - const LoginTitleStyled = styled.div` font-size: 2rem; font-weight: 700; @@ -66,7 +56,7 @@ const LinkStyled = styled(Link)` const LoginButtonStyled = styled.button` width: 350px; - height: 56px; + height: 54px; padding: 0.8rem 1rem; background-color: #9747ff; color: #ffffff; diff --git a/v2/frontend/src/pages/RegisterPage.tsx b/v2/frontend/src/pages/RegisterPage.tsx index cc01838..fdc625d 100644 --- a/v2/frontend/src/pages/RegisterPage.tsx +++ b/v2/frontend/src/pages/RegisterPage.tsx @@ -1,4 +1,5 @@ import { useState } from "react"; +import { ReactComponent as NewYearImg } from "../assets/images/newYear.svg"; import styled from "styled-components"; import UserInputField from "../components/UserInputField"; @@ -8,6 +9,7 @@ const RegisterPage = () => { return ( + CABI-Onboarding Date: Wed, 22 Jan 2025 17:05:42 +0900 Subject: [PATCH 035/104] =?UTF-8?q?FEAT=20:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 헤더에 로그인한 유저의 이름이 담긴 쿠키 넣어서 응답 --- .../user/controller/UserController.java | 16 +++++++++++--- .../user/repository/UserRepository.java | 3 +++ .../user/service/UserService.java | 21 +++++++++++++++---- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java index afa6827..b65a87c 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java @@ -4,6 +4,9 @@ import com.cabi.greetingCard.dto.UserSearchDto; import com.cabi.greetingCard.user.service.UserService; import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseCookie; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -52,8 +55,15 @@ public UserSearchDto searchUser(@RequestParam(name = "name") String name, * @param userInfoDto * @return */ - @PostMapping("/test3") - public void login(@RequestBody UserInfoDto userInfoDto) { - userService.login(userInfoDto.getName(), userInfoDto.getPassword()); + @PostMapping("/login") + public ResponseEntity login(@RequestBody UserInfoDto userInfoDto) { + ResponseCookie cookie = userService.login(userInfoDto.getName(), userInfoDto.getPassword()); + + HttpHeaders headers = new HttpHeaders(); + headers.add(HttpHeaders.SET_COOKIE, cookie.toString()); + + return ResponseEntity.ok() + .headers(headers) + .build(); } } diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/repository/UserRepository.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/repository/UserRepository.java index c2edd8f..3086f0b 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/repository/UserRepository.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/repository/UserRepository.java @@ -1,6 +1,7 @@ package com.cabi.greetingCard.user.repository; import com.cabi.greetingCard.user.domain.User; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @@ -11,4 +12,6 @@ public interface UserRepository extends JpaRepository { boolean existsByName(String name); + + Optional findByName(String name); } diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java index 1f3ac69..ce9184d 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java @@ -4,14 +4,16 @@ import com.cabi.greetingCard.exception.ExceptionStatus; import com.cabi.greetingCard.user.domain.User; import com.cabi.greetingCard.user.repository.UserRepository; -import jakarta.servlet.http.Cookie; import java.util.ArrayList; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseCookie; import org.springframework.stereotype.Service; /** * 이 어노테이션은 또 멀까? 컴포넌트 서치가 머더라? Autowired는 또 머지.. */ +@Slf4j @Service @RequiredArgsConstructor public class UserService { @@ -71,10 +73,21 @@ public void verifyDuplicatedName(String name) { *

* 진짜 있는 유저일까? 비밀번호는 맞게 입력했을까?, 성공하면 쿠키를 주자! */ - public void login(String name, String password) { - User user; + public ResponseCookie login(String name, String password) { + // 진짜 있는 유저일까? + User loginUser = userRepository.findByName(name) + .orElseThrow(ExceptionStatus.NOT_FOUND_USER::asGreetingException); - Cookie cookie = new Cookie("쿠키는 어떻게, 왜 쓰는걸까요?", "파라미터는 뭘 줘야하지?"); + // 비밀번호는 맞게 입력했을까? + if (!loginUser.getPassword().equals(password)) { + throw ExceptionStatus.UNAUTHORIZED_PASSWORD.asGreetingException(); + } + + // 성공하면 쿠키를 주자! + return ResponseCookie.from("name", name) + .maxAge(24 * 60 * 60) // 유효 기간 1일 + .path("/") // 경로 설정 + .build(); } /** From 7159ecccf0633985ffbd1b73d34cdcec20d4b688 Mon Sep 17 00:00:00 2001 From: lamodadite Date: Wed, 22 Jan 2025 17:27:56 +0900 Subject: [PATCH 036/104] =?UTF-8?q?FEAT=20:=20auth=20check=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 쿠키 확인후 존재하는 유저인지 확인 --- .../greetingCard/exception/ExceptionStatus.java | 1 + .../user/controller/UserController.java | 11 +++++++++++ .../greetingCard/user/service/UserService.java | 17 +++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionStatus.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionStatus.java index 5237e4a..8bd1669 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionStatus.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionStatus.java @@ -18,6 +18,7 @@ public enum ExceptionStatus { INVALID_PASSWORD(HttpStatus.BAD_REQUEST, "형식에 맞지 않는 비밀번호입니다."), NOT_FOUND_USER(HttpStatus.NOT_FOUND, "존재하지 않는 유저입니디."), INVALID_FORMAT_MESSAGE(HttpStatus.BAD_REQUEST, "잘못된 형식의 메세지입니다!"), + INVALID_COOKIE(HttpStatus.BAD_REQUEST, "잘못된 쿠키입니다."), ; private final int errorCode; diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java index b65a87c..0a4eaa1 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java @@ -66,4 +66,15 @@ public ResponseEntity login(@RequestBody UserInfoDto userInfoDto) { .headers(headers) .build(); } + + /** + * 요청 헤더에 있는 쿠키를 확인하고 유효한 유저인지 확인합니다. + */ + @GetMapping("/auth") + public ResponseEntity checkAuth( + @CookieValue(value = "name", defaultValue = "none") String name) { + userService.checkAuth(name); + + return ResponseEntity.ok().build(); + } } diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java index ce9184d..e1200ad 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java @@ -99,4 +99,21 @@ public ResponseCookie login(String name, String password) { public UserSearchDto searchUserByName(String name) { return new UserSearchDto(new ArrayList<>()); } + + /** + * 로그인 권한이 필요한 페이지마다 현재 로그인한 유저가 유효한지 확인합니다. + * + * @param name + */ + public void checkAuth(String name) { + // 잘못된 쿠키인 경우 + if (name.equals("none")) { + throw ExceptionStatus.INVALID_COOKIE.asGreetingException(); + } + + // 쿠키에 유저이름이 있지만 데이터베이스와 일치하지 않는 경우 + if (!userRepository.existsByName(name)) { + throw ExceptionStatus.NOT_FOUND_USER.asGreetingException(); + } + } } From 1bb1996243f6e6eedac7a140104e0e10c17826a9 Mon Sep 17 00:00:00 2001 From: lamodadite Date: Wed, 22 Jan 2025 19:18:01 +0900 Subject: [PATCH 037/104] =?UTF-8?q?FEAT=20:=20=EC=9C=A0=EC=A0=80=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EA=B2=80=EC=83=89=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/controller/UserController.java | 12 ++++++++---- .../user/repository/UserRepository.java | 3 +++ .../greetingCard/user/service/UserService.java | 17 +++++++++++++---- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java index 0a4eaa1..0a1af15 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java @@ -37,10 +37,14 @@ public void registerUser(@RequestBody UserInfoDto userInfoDto) { userService.registerUser(userInfoDto.getName(), userInfoDto.getPassword()); } - @GetMapping("/test2") - public UserSearchDto searchUser(@RequestParam(name = "name") String name, - @CookieValue(name = "userName") String userName) { - return userService.searchUserByName(name); + @GetMapping("/search/name") + public ResponseEntity searchUser(@RequestParam(name = "input") String input, + @CookieValue(name = "name") String userName) { + + UserSearchDto users = userService.searchUserByName(input, userName); + + return ResponseEntity.ok() + .body(users); } /** diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/repository/UserRepository.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/repository/UserRepository.java index 3086f0b..e53d645 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/repository/UserRepository.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/repository/UserRepository.java @@ -1,6 +1,7 @@ package com.cabi.greetingCard.user.repository; import com.cabi.greetingCard.user.domain.User; +import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @@ -14,4 +15,6 @@ public interface UserRepository extends JpaRepository { boolean existsByName(String name); Optional findByName(String name); + + List findAllByNameStartingWithOrderByName(String input); } diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java index e1200ad..2af17c2 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java @@ -4,7 +4,8 @@ import com.cabi.greetingCard.exception.ExceptionStatus; import com.cabi.greetingCard.user.domain.User; import com.cabi.greetingCard.user.repository.UserRepository; -import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseCookie; @@ -93,11 +94,19 @@ public ResponseCookie login(String name, String password) { /** * 파라미터를 포함하고 있는 user정보들 중 name만을 List 형식으로 반환 * - * @param name + * @param input + * @param userName * @return */ - public UserSearchDto searchUserByName(String name) { - return new UserSearchDto(new ArrayList<>()); + public UserSearchDto searchUserByName(String input, String userName) { + List userList = userRepository.findAllByNameStartingWithOrderByName(input); + + // 현재 로그인한 userName과 일치한다면 삭제 + userList.removeIf(user -> user.getName().equals(userName)); + + List nameList = userList.stream().map(User::getName).collect(Collectors.toList()); + + return new UserSearchDto(nameList); } /** From 042ea38c9ce5993b0ba2fad072ba7db63a75b386 Mon Sep 17 00:00:00 2001 From: lamodadite Date: Wed, 22 Jan 2025 20:18:31 +0900 Subject: [PATCH 038/104] =?UTF-8?q?FEAT=20:=20=EA=B7=B8=EB=A3=B9=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EA=B2=80=EC=83=89=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cabi/greetingCard/dto/GroupSearchDto.java | 12 +++++++++++ .../user/controller/UserController.java | 12 +++++++++++ .../greetingCard/user/domain/GroupNames.java | 6 ++++++ .../user/service/UserService.java | 20 +++++++++++++++++++ 4 files changed, 50 insertions(+) create mode 100644 v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/dto/GroupSearchDto.java create mode 100644 v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/domain/GroupNames.java diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/dto/GroupSearchDto.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/dto/GroupSearchDto.java new file mode 100644 index 0000000..c7bee5b --- /dev/null +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/dto/GroupSearchDto.java @@ -0,0 +1,12 @@ +package com.cabi.greetingCard.dto; + +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class GroupSearchDto { + + List GroupNames; +} diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java index 0a1af15..96d2b65 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java @@ -1,5 +1,6 @@ package com.cabi.greetingCard.user.controller; +import com.cabi.greetingCard.dto.GroupSearchDto; import com.cabi.greetingCard.dto.UserInfoDto; import com.cabi.greetingCard.dto.UserSearchDto; import com.cabi.greetingCard.user.service.UserService; @@ -47,6 +48,15 @@ public ResponseEntity searchUser(@RequestParam(name = "input") String input, .body(users); } + @GetMapping("/search/group") + public ResponseEntity searchGroup(@RequestParam(name = "input") String input) { + + GroupSearchDto groups = userService.searchGroupByName(input); + + return ResponseEntity.ok() + .body(groups); + } + /** * 로그인 기능 *

@@ -81,4 +91,6 @@ public ResponseEntity checkAuth( return ResponseEntity.ok().build(); } + + } diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/domain/GroupNames.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/domain/GroupNames.java new file mode 100644 index 0000000..e6e0b43 --- /dev/null +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/domain/GroupNames.java @@ -0,0 +1,6 @@ +package com.cabi.greetingCard.user.domain; + +public class GroupNames { + + public static final String GROUP_EVERYONE = "@everyone"; +} diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java index 2af17c2..5ef41a1 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java @@ -1,10 +1,16 @@ package com.cabi.greetingCard.user.service; +import static com.cabi.greetingCard.user.domain.GroupNames.GROUP_EVERYONE; + +import com.cabi.greetingCard.dto.GroupSearchDto; import com.cabi.greetingCard.dto.UserSearchDto; import com.cabi.greetingCard.exception.ExceptionStatus; import com.cabi.greetingCard.user.domain.User; import com.cabi.greetingCard.user.repository.UserRepository; +import jakarta.annotation.PostConstruct; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -21,6 +27,13 @@ public class UserService { private final UserRepository userRepository; + private Set groupNames; + + @PostConstruct + public void init() { + groupNames = new HashSet<>(List.of(GROUP_EVERYONE)); + } + /** * 새로운 유저를 등록합니다 *

@@ -109,6 +122,13 @@ public UserSearchDto searchUserByName(String input, String userName) { return new UserSearchDto(nameList); } + public GroupSearchDto searchGroupByName(String input) { + List list = groupNames.stream().filter(group -> group.startsWith("@" + input)) + .toList(); + + return new GroupSearchDto(list); + } + /** * 로그인 권한이 필요한 페이지마다 현재 로그인한 유저가 유효한지 확인합니다. * From bed73c597cb70ec0a6e8c4a9fe2dab9423632c50 Mon Sep 17 00:00:00 2001 From: lamodadite Date: Wed, 22 Jan 2025 20:55:43 +0900 Subject: [PATCH 039/104] =?UTF-8?q?FIX=20:=20Cookie=20value=EB=A5=BC=20nam?= =?UTF-8?q?e=EC=97=90=EC=84=9C=20userName=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/cabi/greetingCard/user/controller/UserController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java index 96d2b65..01fea51 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java @@ -40,7 +40,7 @@ public void registerUser(@RequestBody UserInfoDto userInfoDto) { @GetMapping("/search/name") public ResponseEntity searchUser(@RequestParam(name = "input") String input, - @CookieValue(name = "name") String userName) { + @CookieValue(name = "userName") String userName) { UserSearchDto users = userService.searchUserByName(input, userName); From 212da6b72a8f1220b1ef31c6b11c62b13835757b Mon Sep 17 00:00:00 2001 From: lamodadite Date: Wed, 22 Jan 2025 21:40:32 +0900 Subject: [PATCH 040/104] =?UTF-8?q?FEAT=20:=20=EB=A9=94=EC=84=B8=EC=A7=80?= =?UTF-8?q?=20=EB=B3=B4=EB=82=B4=EA=B8=B0=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 일단 receiver가 @everyone이면 서비스에서 분기 태워서 모두에게 보내도록 구현함 --- .../message/controller/MessageController.java | 2 +- .../message/service/MessageService.java | 47 +++++++++++++++++-- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/controller/MessageController.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/controller/MessageController.java index d163c5e..59d00b7 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/controller/MessageController.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/controller/MessageController.java @@ -20,7 +20,7 @@ @RestController @RequiredArgsConstructor -@RequestMapping("/주요 리소스가 누구지..") +@RequestMapping("/messages") @Slf4j public class MessageController { diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java index 554938f..d296725 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java @@ -1,5 +1,7 @@ package com.cabi.greetingCard.message.service; +import static com.cabi.greetingCard.user.domain.GroupNames.GROUP_EVERYONE; + import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.model.ObjectMetadata; import com.cabi.greetingCard.dto.MessageRequestDto; @@ -8,6 +10,8 @@ import com.cabi.greetingCard.exception.ExceptionStatus; import com.cabi.greetingCard.message.domain.Message; import com.cabi.greetingCard.message.repository.MessageRepository; +import com.cabi.greetingCard.user.domain.User; +import com.cabi.greetingCard.user.repository.UserRepository; import java.io.IOException; import java.time.LocalDateTime; import java.util.ArrayList; @@ -28,13 +32,21 @@ @Slf4j public class MessageService { - private static final String EVERYONE = "@everyone"; + static final int MESSAGE_LENGTH_LIMIT = 42; private final MessageRepository messageRepository; private final AmazonS3 s3Client; + private final UserRepository userRepository; @Value("${cloud.aws.s3.bucket}") private String bucketName; + private static void verifyValidMessageFormat(MessageRequestDto messageData) { + if (messageData.getContext().isEmpty() + || messageData.getContext().length() > MESSAGE_LENGTH_LIMIT) { + throw ExceptionStatus.INVALID_FORMAT_MESSAGE.asGreetingException(); + } + } + /** * 메세지 보내기 * @@ -46,12 +58,39 @@ public class MessageService { public void sendMessage(String userName, MessageRequestDto messageData) throws IOException { String imageUrl = saveImage(messageData.getImage()); + if (messageData.getReceiverName().equals(GROUP_EVERYONE)) { + sendMessageToAll(messageData, userName, imageUrl); + return; + } + + verifyExistUser(messageData); + verifyValidMessageFormat(messageData); + Message message = Message.of(userName, messageData.getReceiverName(), messageData.getContext(), imageUrl, LocalDateTime.now()); + messageRepository.save(message); } + private void verifyExistUser(MessageRequestDto messageData) { + if (!userRepository.existsByName(messageData.getReceiverName())) { + throw ExceptionStatus.NOT_FOUND_USER.asGreetingException(); + } + } + + @Transactional + public void sendMessageToAll(MessageRequestDto messageData, String userName, String imageUrl) { + verifyValidMessageFormat(messageData); + + List userList = userRepository.findAll(); + userList.removeIf(user -> user.getName().equals(userName)); + List messageList = userList.stream() + .map(user -> Message.of(userName, user.getName(), + messageData.getContext(), imageUrl, LocalDateTime.now())) + .toList(); + messageRepository.saveAll(messageList); + } /** * 수정할 부분 없읍니다! @@ -68,9 +107,11 @@ public String saveImage(Optional imageFile) throws IOException { } MultipartFile image = imageFile.get(); String originalFilename = image.getOriginalFilename(); - String extension = originalFilename.contains(".") ? originalFilename.substring(originalFilename.lastIndexOf(".")) : ""; + String extension = originalFilename.contains(".") ? originalFilename.substring( + originalFilename.lastIndexOf(".")) : ""; verifyExtensionType(extension); - String safeFileName = originalFilename.length() > 10 ? originalFilename.substring(0, 10) : originalFilename; + String safeFileName = originalFilename.length() > 10 ? originalFilename.substring(0, 10) + : originalFilename; String s3FileName = UUID.randomUUID() + safeFileName + extension; ObjectMetadata objectMetadata = new ObjectMetadata(); From c46ab82c49557b9b0d597b2572e3377a03ae03aa Mon Sep 17 00:00:00 2001 From: lamodadite Date: Wed, 22 Jan 2025 21:44:28 +0900 Subject: [PATCH 041/104] =?UTF-8?q?FIX=20:=20verifyNameLengthUnder10=20->?= =?UTF-8?q?=20verifyNameLength=EB=A1=9C=20=EB=B3=80=EA=B2=BD,=20=EC=83=81?= =?UTF-8?q?=EC=88=98=EA=B0=92=20=EB=B3=80=EC=88=98=EB=A1=9C=20=EB=BA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cabi/greetingCard/user/service/UserService.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java index 5ef41a1..3cfb291 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java @@ -25,8 +25,9 @@ @RequiredArgsConstructor public class UserService { + private static final int USER_NAME_LENGTH_LIMIT = 10; + private static final int COOKIE_MAX_AGE = 24 * 60 * 60; private final UserRepository userRepository; - private Set groupNames; @PostConstruct @@ -43,7 +44,7 @@ public void init() { */ public void registerUser(String name, String password) { verifyDuplicatedName(name); - verifyNameLengthUnder10(name); + verifyNameLength(name); verifyNameIsNumericOrAlphabet(name); // 하나의 메서드로 따로 빼는게 나을까? 코드만 복잡해지는게 아닌지? User user = new User(name, password); userRepository.save(user); @@ -65,8 +66,8 @@ private void verifyNameIsNumericOrAlphabet(String name) { * * @param name */ - private void verifyNameLengthUnder10(String name) { - if (name.length() > 10) { + private void verifyNameLength(String name) { + if (name.length() > USER_NAME_LENGTH_LIMIT) { throw ExceptionStatus.INVALID_NAME.asGreetingException(); //TODO : 모든 exception에 에러코드 넣어야 함 } } @@ -99,7 +100,7 @@ public ResponseCookie login(String name, String password) { // 성공하면 쿠키를 주자! return ResponseCookie.from("name", name) - .maxAge(24 * 60 * 60) // 유효 기간 1일 + .maxAge(COOKIE_MAX_AGE) // 유효 기간 1일 .path("/") // 경로 설정 .build(); } From 530ecc6a7481ccb81d70b18369dfeba0aec8ed54 Mon Sep 17 00:00:00 2001 From: lamodadite Date: Wed, 22 Jan 2025 22:00:03 +0900 Subject: [PATCH 042/104] =?UTF-8?q?FIX=20:=20@everyone=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EB=93=A4=EC=96=B4=EC=99=94=EC=9D=84=EB=95=8C=20reciever?= =?UTF-8?q?=EC=97=90=20@everyone=EC=9D=B4=20=EA=B7=B8=EB=8C=80=EB=A1=9C=20?= =?UTF-8?q?=EB=93=A4=EC=96=B4=EA=B0=80=EB=8F=84=EB=A1=9D=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/cabi/greetingCard/message/service/MessageService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java index d296725..ce81997 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java @@ -86,7 +86,7 @@ public void sendMessageToAll(MessageRequestDto messageData, String userName, Str List userList = userRepository.findAll(); userList.removeIf(user -> user.getName().equals(userName)); List messageList = userList.stream() - .map(user -> Message.of(userName, user.getName(), + .map(user -> Message.of(userName, messageData.getReceiverName(), messageData.getContext(), imageUrl, LocalDateTime.now())) .toList(); messageRepository.saveAll(messageList); From 8c066f21ab2831c8b6bc3d18e580c67008df59f9 Mon Sep 17 00:00:00 2001 From: lamodadite Date: Wed, 22 Jan 2025 23:23:27 +0900 Subject: [PATCH 043/104] =?UTF-8?q?FEAT=20:=20=EB=A9=94=EC=84=B8=EC=A7=80?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 미리 작성된 틀과 구조가 달라서, 틀은 그대로 남겨두고 새로 기능들을 구현했음 --- .../dto/MessageResponsePaginationDto.java | 2 +- .../exception/ExceptionStatus.java | 2 +- .../message/controller/MessageController.java | 13 ++- .../message/domain/MessageCategory.java | 16 ++++ .../message/repository/MessageRepository.java | 5 ++ .../message/service/MessageService.java | 87 ++++++++++++++++++- 6 files changed, 117 insertions(+), 8 deletions(-) create mode 100644 v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/domain/MessageCategory.java diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/dto/MessageResponsePaginationDto.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/dto/MessageResponsePaginationDto.java index 3699993..eddb07f 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/dto/MessageResponsePaginationDto.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/dto/MessageResponsePaginationDto.java @@ -9,5 +9,5 @@ public class MessageResponsePaginationDto { private List messages; - private Long totalLength; + private int totalLength; } diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionStatus.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionStatus.java index 8bd1669..c574b2c 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionStatus.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionStatus.java @@ -19,7 +19,7 @@ public enum ExceptionStatus { NOT_FOUND_USER(HttpStatus.NOT_FOUND, "존재하지 않는 유저입니디."), INVALID_FORMAT_MESSAGE(HttpStatus.BAD_REQUEST, "잘못된 형식의 메세지입니다!"), INVALID_COOKIE(HttpStatus.BAD_REQUEST, "잘못된 쿠키입니다."), - ; + INVALID_QUERYSTRING(HttpStatus.BAD_REQUEST, "잘못된 쿼리스트링입니다."); private final int errorCode; private final String message; diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/controller/MessageController.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/controller/MessageController.java index 59d00b7..95db4a1 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/controller/MessageController.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/controller/MessageController.java @@ -8,6 +8,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; @@ -46,13 +47,19 @@ public void sendMessage(@CookieValue(name = "userName") String userName, * * @param userName * @param pageable + * @param category * @return */ @GetMapping("") - public MessageResponsePaginationDto getAllMessages( + public ResponseEntity getAllMessages( @CookieValue(name = "userName") String userName, - Pageable pageable) { - return messageService.getEveryoneMessage(userName, pageable); + Pageable pageable, + int category) { + MessageResponsePaginationDto messages = messageService.getMessages(userName, pageable, + category); + + return ResponseEntity.ok() + .body(messages); } /** diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/domain/MessageCategory.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/domain/MessageCategory.java new file mode 100644 index 0000000..9351508 --- /dev/null +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/domain/MessageCategory.java @@ -0,0 +1,16 @@ +package com.cabi.greetingCard.message.domain; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public enum MessageCategory { + + TO_EVERYONE("@everyone", 0), + TO_ME("to_me", 1), + FROM_ME("from_me", 2); + + private final String name; + private final int number; +} diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/repository/MessageRepository.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/repository/MessageRepository.java index f416080..e3b9254 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/repository/MessageRepository.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/repository/MessageRepository.java @@ -1,10 +1,15 @@ package com.cabi.greetingCard.message.repository; import com.cabi.greetingCard.message.domain.Message; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface MessageRepository extends JpaRepository { + Page findAllByReceiverName(String receiverName, Pageable pageable); + + Page findAllBySenderName(String name, Pageable pageable); } diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java index ce81997..26bacf0 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java @@ -8,7 +8,9 @@ import com.cabi.greetingCard.dto.MessageResponseDto; import com.cabi.greetingCard.dto.MessageResponsePaginationDto; import com.cabi.greetingCard.exception.ExceptionStatus; +import com.cabi.greetingCard.mapper.MessageMapper; import com.cabi.greetingCard.message.domain.Message; +import com.cabi.greetingCard.message.domain.MessageCategory; import com.cabi.greetingCard.message.repository.MessageRepository; import com.cabi.greetingCard.user.domain.User; import com.cabi.greetingCard.user.repository.UserRepository; @@ -22,7 +24,9 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; @@ -37,6 +41,7 @@ public class MessageService { private final MessageRepository messageRepository; private final AmazonS3 s3Client; private final UserRepository userRepository; + private final MessageMapper messageMapper; @Value("${cloud.aws.s3.bucket}") private String bucketName; @@ -143,7 +148,7 @@ public MessageResponsePaginationDto getEveryoneMessage(String userName, Pageable Page receivedMessages; List messageDtos = new ArrayList<>(); - return new MessageResponsePaginationDto(messageDtos, 0L); + return new MessageResponsePaginationDto(messageDtos, 0); } /** @@ -157,7 +162,7 @@ public MessageResponsePaginationDto getReceivedMessages(String userName, Pageabl Page receivedMessages; List messageDtos = new ArrayList<>(); - return new MessageResponsePaginationDto(messageDtos, 0L); + return new MessageResponsePaginationDto(messageDtos, 0); } /** @@ -170,7 +175,7 @@ public MessageResponsePaginationDto getReceivedMessages(String userName, Pageabl public MessageResponsePaginationDto getSentMessages(String userName, Pageable pageable) { Page sentMessages; List messageDtos = new ArrayList<>(); - return new MessageResponsePaginationDto(messageDtos, 0L); + return new MessageResponsePaginationDto(messageDtos, 0); } /** @@ -188,5 +193,81 @@ public void updateMessageContext(String userName, Long messageId, String context .orElseThrow(ExceptionStatus.NOT_FOUND_MESSAGE::asGreetingException); message.updateContext(context); } + + /** + * 조건에 맞는 메세지 리스트를 반환합니다. 페이징 처리가 되며 카테고리마다 다른 결과를 반환합니다. + * + * @param userName + * @param pageable + * @return + */ + @Transactional + public MessageResponsePaginationDto getMessages(String userName, Pageable pageable, + int category) { + PageRequest pageRequest = PageRequest.of(pageable.getPageNumber() - 1, + pageable.getPageSize(), + Sort.by("created").descending()); + Page messageList = splitByCategory(userName, category, pageRequest); + List messageResponseDtoList = messageList.stream() + .map(message -> messageMapper.toMessageResponseDto(message, + userName.equals(message.getSenderName()))).toList(); + return new MessageResponsePaginationDto(messageResponseDtoList, + messageList.getTotalPages()); + } + + /** + * 카테고리에 맞게 데이터를 조회하는 메서드를 호출하고 리턴값을 반환합니다. + * + * @param userName + * @param category + * @param pageRequest + * @return + */ + private Page splitByCategory(String userName, int category, PageRequest pageRequest) { + Page messageList; + if (category == MessageCategory.TO_EVERYONE.getNumber()) { + messageList = getMessagesSendEveryone(pageRequest); + } else if (category == MessageCategory.TO_ME.getNumber()) { + messageList = getMessagesSendMe(userName, pageRequest); + } else if (category == MessageCategory.FROM_ME.getNumber()) { + messageList = getMessagesFromMe(userName, pageRequest); + } else { + throw ExceptionStatus.INVALID_QUERYSTRING.asGreetingException(); + } + return messageList; + } + + /** + * 내가 보낸 메세지들을 조회합니다. + * + * @param userName + * @param pageRequest + * @return + */ + private Page getMessagesFromMe(String userName, PageRequest pageRequest) { + return messageRepository.findAllBySenderName(userName, pageRequest); + } + + /** + * 모두에게 보내진 메세지들을 조회합니다. + * + * @param pageRequest + * @return + */ + private Page getMessagesSendEveryone(PageRequest pageRequest) { + return messageRepository.findAllByReceiverName(MessageCategory.TO_EVERYONE.getName(), + pageRequest); + } + + /** + * 나에게 보내진 메세지들을 조회합니다. + * + * @param userName + * @param pageRequest + * @return + */ + private Page getMessagesSendMe(String userName, PageRequest pageRequest) { + return messageRepository.findAllByReceiverName(userName, pageRequest); + } } From 0ad404811c754c747f5850585eb43e6042651481 Mon Sep 17 00:00:00 2001 From: lamodadite Date: Wed, 22 Jan 2025 23:41:36 +0900 Subject: [PATCH 044/104] =?UTF-8?q?FEAT=20:=20=EB=A9=94=EC=84=B8=EC=A7=80?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/ExceptionStatus.java | 1 + .../message/controller/MessageController.java | 16 ++++++++++++---- .../greetingCard/message/domain/Message.java | 3 +-- .../message/service/MessageService.java | 18 +++++++++++++----- 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionStatus.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionStatus.java index c574b2c..1b676af 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionStatus.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionStatus.java @@ -13,6 +13,7 @@ public enum ExceptionStatus { NOT_FOUND_MESSAGE(HttpStatus.NOT_FOUND, "존재하지 않는 메세지입니다"), UNAUTHORIZED_PASSWORD(HttpStatus.UNAUTHORIZED, "비밀번호가 일치하지 않습니다"), + UNAUTHORIZED_USER(HttpStatus.UNAUTHORIZED, "권한이 없는 유저입니다."), DUPLICATED_NAME(HttpStatus.UNAUTHORIZED, "중복된 아이디입니다."), INVALID_NAME(HttpStatus.BAD_REQUEST, "형식에 맞지 않는 아이디입니다."), INVALID_PASSWORD(HttpStatus.BAD_REQUEST, "형식에 맞지 않는 비밀번호입니다."), diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/controller/MessageController.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/controller/MessageController.java index 95db4a1..8833d2c 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/controller/MessageController.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/controller/MessageController.java @@ -12,9 +12,9 @@ import org.springframework.web.bind.annotation.CookieValue; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -89,10 +89,18 @@ public MessageResponsePaginationDto getSentMessages( return messageService.getSentMessages(userName, pageable); } - @PatchMapping("/{test4}") - public void updateMessageContext(@CookieValue(name = "userName") String userName, - @PathVariable(name = "test?") Long messageId, + /** + * 메세지 내용을 수정합니다. + * + * @param userName + * @param messageId + * @param requestDto + */ + @PutMapping("/{messageId}") + public ResponseEntity updateMessageContext(@CookieValue(name = "userName") String userName, + @PathVariable(name = "messageId") Long messageId, @RequestBody RequestDto requestDto) { messageService.updateMessageContext(userName, messageId, requestDto.getContext()); + return ResponseEntity.ok().build(); } } diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/domain/Message.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/domain/Message.java index bd29422..40063ea 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/domain/Message.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/domain/Message.java @@ -8,8 +8,6 @@ import jakarta.persistence.Id; import jakarta.persistence.Table; import java.time.LocalDateTime; -import java.util.Arrays; -import java.util.List; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -66,5 +64,6 @@ private boolean isValid() { } public void updateContext(String context) { + this.context = context; } } diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java index 26bacf0..52aeb30 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java @@ -45,13 +45,19 @@ public class MessageService { @Value("${cloud.aws.s3.bucket}") private String bucketName; - private static void verifyValidMessageFormat(MessageRequestDto messageData) { - if (messageData.getContext().isEmpty() - || messageData.getContext().length() > MESSAGE_LENGTH_LIMIT) { + private static void verifyValidMessageFormat(String context) { + if (context.isEmpty() + || context.length() > MESSAGE_LENGTH_LIMIT) { throw ExceptionStatus.INVALID_FORMAT_MESSAGE.asGreetingException(); } } + private static void verifyUserAuthorized(String userName, Message message) { + if (!message.getSenderName().equals(userName)) { + throw ExceptionStatus.UNAUTHORIZED_USER.asGreetingException(); + } + } + /** * 메세지 보내기 * @@ -69,7 +75,7 @@ public void sendMessage(String userName, MessageRequestDto messageData) throws I } verifyExistUser(messageData); - verifyValidMessageFormat(messageData); + verifyValidMessageFormat(messageData.getContext()); Message message = Message.of(userName, messageData.getReceiverName(), messageData.getContext(), @@ -86,7 +92,7 @@ private void verifyExistUser(MessageRequestDto messageData) { @Transactional public void sendMessageToAll(MessageRequestDto messageData, String userName, String imageUrl) { - verifyValidMessageFormat(messageData); + verifyValidMessageFormat(messageData.getContext()); List userList = userRepository.findAll(); userList.removeIf(user -> user.getName().equals(userName)); @@ -191,6 +197,8 @@ public MessageResponsePaginationDto getSentMessages(String userName, Pageable pa public void updateMessageContext(String userName, Long messageId, String context) { Message message = messageRepository.findById(messageId) .orElseThrow(ExceptionStatus.NOT_FOUND_MESSAGE::asGreetingException); + verifyUserAuthorized(userName, message); + verifyValidMessageFormat(context); message.updateContext(context); } From d5ca47527618fa89cdf81caff1aec60fec565b55 Mon Sep 17 00:00:00 2001 From: lamodadite Date: Thu, 23 Jan 2025 13:41:20 +0900 Subject: [PATCH 045/104] =?UTF-8?q?FIX=20:=20=EC=BF=A0=ED=82=A4=20?= =?UTF-8?q?=EC=B2=B4=ED=81=AC=20=EB=B6=80=EB=B6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/cabi/greetingCard/user/controller/UserController.java | 2 +- .../java/com/cabi/greetingCard/user/service/UserService.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java index 01fea51..c838409 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java @@ -86,7 +86,7 @@ public ResponseEntity login(@RequestBody UserInfoDto userInfoDto) { */ @GetMapping("/auth") public ResponseEntity checkAuth( - @CookieValue(value = "name", defaultValue = "none") String name) { + @CookieValue(value = "name", required = false) String name) { userService.checkAuth(name); return ResponseEntity.ok().build(); diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java index 3cfb291..d510f63 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java @@ -137,7 +137,7 @@ public GroupSearchDto searchGroupByName(String input) { */ public void checkAuth(String name) { // 잘못된 쿠키인 경우 - if (name.equals("none")) { + if (name == null) { throw ExceptionStatus.INVALID_COOKIE.asGreetingException(); } From fdba3bc4e5d63eddb5bb50d8f9aa1d101cdf2022 Mon Sep 17 00:00:00 2001 From: lamodadite Date: Thu, 23 Jan 2025 13:47:50 +0900 Subject: [PATCH 046/104] =?UTF-8?q?FIX=20:=20=EC=BF=A0=ED=82=A4=20?= =?UTF-8?q?=EB=B0=B8=EB=A5=98=20name=20->=20userName?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/cabi/greetingCard/user/service/UserService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java index d510f63..b432f26 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java @@ -99,7 +99,7 @@ public ResponseCookie login(String name, String password) { } // 성공하면 쿠키를 주자! - return ResponseCookie.from("name", name) + return ResponseCookie.from("userName", name) .maxAge(COOKIE_MAX_AGE) // 유효 기간 1일 .path("/") // 경로 설정 .build(); From 8c4ae99a9b1759cb7fb56af9473aa8bd1a03c474 Mon Sep 17 00:00:00 2001 From: lamodadite Date: Thu, 23 Jan 2025 13:57:58 +0900 Subject: [PATCH 047/104] =?UTF-8?q?REFACTOR=20:=20=EA=B0=99=EC=9D=80=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=EC=9D=84=20=ED=95=98=EB=8A=94=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EA=B0=84=EA=B2=B0=ED=95=98=EA=B2=8C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cabi/greetingCard/user/service/UserService.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java index b432f26..5eef190 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java @@ -11,7 +11,6 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseCookie; @@ -113,12 +112,10 @@ public ResponseCookie login(String name, String password) { * @return */ public UserSearchDto searchUserByName(String input, String userName) { - List userList = userRepository.findAllByNameStartingWithOrderByName(input); - - // 현재 로그인한 userName과 일치한다면 삭제 - userList.removeIf(user -> user.getName().equals(userName)); - - List nameList = userList.stream().map(User::getName).collect(Collectors.toList()); + List nameList = userRepository.findAllByNameStartingWithOrderByName(input).stream() + .map(User::getName) + .filter(name -> !name.equals(userName)) + .toList(); return new UserSearchDto(nameList); } From dd828b0c1648de42d5d7b1bc1b30bb5687df92f0 Mon Sep 17 00:00:00 2001 From: lamodadite Date: Thu, 23 Jan 2025 15:55:17 +0900 Subject: [PATCH 048/104] =?UTF-8?q?FIX=20:=20Exception=EC=97=90=20errorCod?= =?UTF-8?q?e=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/ExceptionController.java | 2 +- .../exception/ExceptionStatus.java | 28 ++++++++++--------- .../message/service/MessageService.java | 14 +++++++++- .../user/controller/UserController.java | 2 +- .../user/service/UserService.java | 6 ++-- .../src/main/resources/application.yml | 4 +++ 6 files changed, 37 insertions(+), 19 deletions(-) diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionController.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionController.java index 08100b1..1cd7d97 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionController.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionController.java @@ -15,7 +15,7 @@ public class ExceptionController { @ExceptionHandler(GreetingException.class) public ResponseEntity serviceExceptionHandler(GreetingException e) { return ResponseEntity - .status(e.status.getErrorCode()) + .status(e.status.getHttpCode()) .body(e.status); } } diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionStatus.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionStatus.java index 1b676af..c111b1c 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionStatus.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionStatus.java @@ -11,25 +11,27 @@ @Getter public enum ExceptionStatus { - NOT_FOUND_MESSAGE(HttpStatus.NOT_FOUND, "존재하지 않는 메세지입니다"), - UNAUTHORIZED_PASSWORD(HttpStatus.UNAUTHORIZED, "비밀번호가 일치하지 않습니다"), - UNAUTHORIZED_USER(HttpStatus.UNAUTHORIZED, "권한이 없는 유저입니다."), - DUPLICATED_NAME(HttpStatus.UNAUTHORIZED, "중복된 아이디입니다."), - INVALID_NAME(HttpStatus.BAD_REQUEST, "형식에 맞지 않는 아이디입니다."), - INVALID_PASSWORD(HttpStatus.BAD_REQUEST, "형식에 맞지 않는 비밀번호입니다."), - NOT_FOUND_USER(HttpStatus.NOT_FOUND, "존재하지 않는 유저입니디."), - INVALID_FORMAT_MESSAGE(HttpStatus.BAD_REQUEST, "잘못된 형식의 메세지입니다!"), - INVALID_COOKIE(HttpStatus.BAD_REQUEST, "잘못된 쿠키입니다."), - INVALID_QUERYSTRING(HttpStatus.BAD_REQUEST, "잘못된 쿼리스트링입니다."); + LOGIN_FAIL(HttpStatus.UNAUTHORIZED, "로그인에 실패했습니다.", "001"), + DUPLICATED_NAME(HttpStatus.UNAUTHORIZED, "중복된 아이디입니다.", "002"), + INVALID_NAME(HttpStatus.BAD_REQUEST, "형식에 맞지 않는 아이디입니다.", "003"), + INVALID_PASSWORD(HttpStatus.BAD_REQUEST, "형식에 맞지 않는 비밀번호입니다.", "004"), + INVALID_GROUP_ACCESS(HttpStatus.BAD_REQUEST, "잘못된 접근입니다.", "005"), + INVALID_FORMAT_MESSAGE(HttpStatus.BAD_REQUEST, "잘못된 형식의 메세지입니다!", "007"), + NOT_FOUND_USER(HttpStatus.NOT_FOUND, "존재하지 않는 유저입니디.", "008"), + INVALID_QUERYSTRING(HttpStatus.BAD_REQUEST, "잘못된 쿼리스트링입니다.", "009"), + UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "권한이 없습니다.", "010"), + NOT_FOUND_MESSAGE(HttpStatus.NOT_FOUND, "존재하지 않는 메세지입니다", "011"); - private final int errorCode; + private final int httpCode; private final String message; private final String error; + private final String errorCode; - ExceptionStatus(HttpStatus status, String message) { - this.errorCode = status.value(); + ExceptionStatus(HttpStatus status, String message, String errorCode) { + this.httpCode = status.value(); this.message = message; this.error = status.getReasonPhrase(); + this.errorCode = errorCode; } public GreetingException asGreetingException() { diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java index 52aeb30..1003710 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java @@ -54,7 +54,13 @@ private static void verifyValidMessageFormat(String context) { private static void verifyUserAuthorized(String userName, Message message) { if (!message.getSenderName().equals(userName)) { - throw ExceptionStatus.UNAUTHORIZED_USER.asGreetingException(); + throw ExceptionStatus.UNAUTHORIZED.asGreetingException(); + } + } + + private static void verifyValidPageInfo(Pageable pageable) { + if (pageable.getPageNumber() < 0 || pageable.getPageSize() <= 0) { + throw ExceptionStatus.INVALID_QUERYSTRING.asGreetingException(); } } @@ -212,13 +218,19 @@ public void updateMessageContext(String userName, Long messageId, String context @Transactional public MessageResponsePaginationDto getMessages(String userName, Pageable pageable, int category) { + verifyValidPageInfo(pageable); + + // pageNumber는 프론트에서 1부터 인덱싱하기 때문에 -1을 해주고 있음 PageRequest pageRequest = PageRequest.of(pageable.getPageNumber() - 1, pageable.getPageSize(), Sort.by("created").descending()); + Page messageList = splitByCategory(userName, category, pageRequest); + List messageResponseDtoList = messageList.stream() .map(message -> messageMapper.toMessageResponseDto(message, userName.equals(message.getSenderName()))).toList(); + return new MessageResponsePaginationDto(messageResponseDtoList, messageList.getTotalPages()); } diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java index c838409..fafb5f1 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java @@ -86,7 +86,7 @@ public ResponseEntity login(@RequestBody UserInfoDto userInfoDto) { */ @GetMapping("/auth") public ResponseEntity checkAuth( - @CookieValue(value = "name", required = false) String name) { + @CookieValue(value = "userName", required = false) String name) { userService.checkAuth(name); return ResponseEntity.ok().build(); diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java index 5eef190..8724114 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java @@ -90,11 +90,11 @@ public void verifyDuplicatedName(String name) { public ResponseCookie login(String name, String password) { // 진짜 있는 유저일까? User loginUser = userRepository.findByName(name) - .orElseThrow(ExceptionStatus.NOT_FOUND_USER::asGreetingException); + .orElseThrow(ExceptionStatus.LOGIN_FAIL::asGreetingException); // 비밀번호는 맞게 입력했을까? if (!loginUser.getPassword().equals(password)) { - throw ExceptionStatus.UNAUTHORIZED_PASSWORD.asGreetingException(); + throw ExceptionStatus.LOGIN_FAIL.asGreetingException(); } // 성공하면 쿠키를 주자! @@ -135,7 +135,7 @@ public GroupSearchDto searchGroupByName(String input) { public void checkAuth(String name) { // 잘못된 쿠키인 경우 if (name == null) { - throw ExceptionStatus.INVALID_COOKIE.asGreetingException(); + throw ExceptionStatus.UNAUTHORIZED.asGreetingException(); } // 쿠키에 유저이름이 있지만 데이터베이스와 일치하지 않는 경우 diff --git a/v2/backend/greetingCard/src/main/resources/application.yml b/v2/backend/greetingCard/src/main/resources/application.yml index 230d477..e7fc95d 100644 --- a/v2/backend/greetingCard/src/main/resources/application.yml +++ b/v2/backend/greetingCard/src/main/resources/application.yml @@ -28,6 +28,10 @@ spring: hibernate: show_sql: true #콘솔에 로그가 나옴 format_sql: true #이쁘게 해줌 + data: + web: + pageable: + default-page-size: 5 cloud: aws: From f9d5f60eeaeebb3d684687149052df4d27a1127c Mon Sep 17 00:00:00 2001 From: lamodadite Date: Thu, 23 Jan 2025 16:08:32 +0900 Subject: [PATCH 049/104] =?UTF-8?q?DOCS=20:=20=EA=B8=B0=EB=8A=A5=EB=A7=88?= =?UTF-8?q?=EB=8B=A4=20=EC=A3=BC=EC=84=9D=20=EB=8B=AC=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../message/controller/MessageController.java | 27 ------- .../message/service/MessageService.java | 65 ++++------------ .../user/controller/UserController.java | 13 ++++ .../user/service/UserService.java | 76 ++++++++++--------- .../src/main/resources/application.yml | 5 ++ 5 files changed, 74 insertions(+), 112 deletions(-) diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/controller/MessageController.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/controller/MessageController.java index 8833d2c..a99ac6e 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/controller/MessageController.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/controller/MessageController.java @@ -62,33 +62,6 @@ public ResponseEntity getAllMessages( .body(messages); } - /** - * 모두에게 덕담 메세지 보내기 - * - * @param userName - * @param message - * @throws IOException - */ - @PostMapping("/test1") - public void postAllUsers(@CookieValue(name = "userName") String userName, - @ModelAttribute MessageRequestDto message) throws IOException { - messageService.sendMessage(userName, message); - } - - @GetMapping("/test2") - public MessageResponsePaginationDto getReceivedMessages( - @CookieValue(name = "userName") String userName, - Pageable pageable) { - return messageService.getReceivedMessages(userName, pageable); - } - - @GetMapping("/test3") - public MessageResponsePaginationDto getSentMessages( - @CookieValue(name = "userName") String userName, - Pageable pageable) { - return messageService.getSentMessages(userName, pageable); - } - /** * 메세지 내용을 수정합니다. * diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java index 1003710..1011b9e 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java @@ -16,7 +16,6 @@ import com.cabi.greetingCard.user.repository.UserRepository; import java.io.IOException; import java.time.LocalDateTime; -import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -90,12 +89,13 @@ public void sendMessage(String userName, MessageRequestDto messageData) throws I messageRepository.save(message); } - private void verifyExistUser(MessageRequestDto messageData) { - if (!userRepository.existsByName(messageData.getReceiverName())) { - throw ExceptionStatus.NOT_FOUND_USER.asGreetingException(); - } - } - + /** + * 로그인한 유저를 제외한 모두에게 메세지를 보냅니다. + * + * @param messageData + * @param userName + * @param imageUrl + */ @Transactional public void sendMessageToAll(MessageRequestDto messageData, String userName, String imageUrl) { verifyValidMessageFormat(messageData.getContext()); @@ -146,50 +146,6 @@ private void verifyExtensionType(String extension) { } } - /** - * receiverName을 everyone으로 받은 메세지 조회 - *

- * dto는 왜쓰는걸까용? - * - * @param userName - * @param page - * @param size - * @return - */ - public MessageResponsePaginationDto getEveryoneMessage(String userName, Pageable pageable) { - Page receivedMessages; - - List messageDtos = new ArrayList<>(); - return new MessageResponsePaginationDto(messageDtos, 0); - } - - /** - * 내가 받은 메세지 조회 - * - * @param userName - * @param pageable - * @return - */ - public MessageResponsePaginationDto getReceivedMessages(String userName, Pageable pageable) { - Page receivedMessages; - - List messageDtos = new ArrayList<>(); - return new MessageResponsePaginationDto(messageDtos, 0); - } - - /** - * 내가 보낸 메세지 조회 - * - * @param userName - * @param pageable - * @return - */ - public MessageResponsePaginationDto getSentMessages(String userName, Pageable pageable) { - Page sentMessages; - List messageDtos = new ArrayList<>(); - return new MessageResponsePaginationDto(messageDtos, 0); - } - /** * 내가 보낸 메세지 수정 *

@@ -289,5 +245,12 @@ private Page getMessagesSendEveryone(PageRequest pageRequest) { private Page getMessagesSendMe(String userName, PageRequest pageRequest) { return messageRepository.findAllByReceiverName(userName, pageRequest); } + + private void verifyExistUser(MessageRequestDto messageData) { + if (!userRepository.existsByName(messageData.getReceiverName())) { + throw ExceptionStatus.NOT_FOUND_USER.asGreetingException(); + } + } + } diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java index fafb5f1..7b1e422 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/controller/UserController.java @@ -38,6 +38,13 @@ public void registerUser(@RequestBody UserInfoDto userInfoDto) { userService.registerUser(userInfoDto.getName(), userInfoDto.getPassword()); } + /** + * input값으로 시작하는 유저들의 목록을 반환합니다. + * + * @param input + * @param userName + * @return + */ @GetMapping("/search/name") public ResponseEntity searchUser(@RequestParam(name = "input") String input, @CookieValue(name = "userName") String userName) { @@ -48,6 +55,12 @@ public ResponseEntity searchUser(@RequestParam(name = "input") String input, .body(users); } + /** + * input값으로 시작하는 그룹들의 목록을 반환합니다. + * + * @param input + * @return + */ @GetMapping("/search/group") public ResponseEntity searchGroup(@RequestParam(name = "input") String input) { diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java index 8724114..ee47785 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java @@ -49,39 +49,6 @@ public void registerUser(String name, String password) { userRepository.save(user); } - /** - * 유저 이름이 영어 + 숫자로만 구성되어 있는지 확인합니다. - * - * @param name - */ - private void verifyNameIsNumericOrAlphabet(String name) { - if (name == null || !name.matches("^[a-zA-Z0-9]+$")) { - throw ExceptionStatus.INVALID_NAME.asGreetingException(); - } - } - - /** - * 유저 이름의 길이가 10자 이내인지 확인합니다 - * - * @param name - */ - private void verifyNameLength(String name) { - if (name.length() > USER_NAME_LENGTH_LIMIT) { - throw ExceptionStatus.INVALID_NAME.asGreetingException(); //TODO : 모든 exception에 에러코드 넣어야 함 - } - } - - /** - * 중복된 name 검증 기능 - * - * @param name - */ - public void verifyDuplicatedName(String name) { - if (userRepository.existsByName(name)) { - throw ExceptionStatus.DUPLICATED_NAME.asGreetingException(); - } - } - /** * 로그인을 시도합니다 *

@@ -105,7 +72,7 @@ public ResponseCookie login(String name, String password) { } /** - * 파라미터를 포함하고 있는 user정보들 중 name만을 List 형식으로 반환 + * input값으로 시작하는 유저들의 이름 목록을 반환합니다. 로그인한 유저의 이름은 제외됩니다. * * @param input * @param userName @@ -120,6 +87,12 @@ public UserSearchDto searchUserByName(String input, String userName) { return new UserSearchDto(nameList); } + /** + * input값으로 시작하는 그룹들의 목록을 반환합니다. + * + * @param input + * @return + */ public GroupSearchDto searchGroupByName(String input) { List list = groupNames.stream().filter(group -> group.startsWith("@" + input)) .toList(); @@ -143,4 +116,39 @@ public void checkAuth(String name) { throw ExceptionStatus.NOT_FOUND_USER.asGreetingException(); } } + + + /** + * 유저 이름이 영어 + 숫자로만 구성되어 있는지 확인합니다. + * + * @param name + */ + private void verifyNameIsNumericOrAlphabet(String name) { + if (name == null || !name.matches("^[a-zA-Z0-9]+$")) { + throw ExceptionStatus.INVALID_NAME.asGreetingException(); + } + } + + /** + * 유저 이름의 길이가 10자 이내인지 확인합니다 + * + * @param name + */ + private void verifyNameLength(String name) { + if (name.length() > USER_NAME_LENGTH_LIMIT) { + throw ExceptionStatus.INVALID_NAME.asGreetingException(); + } + } + + /** + * 중복된 name 검증 기능 + * + * @param name + */ + public void verifyDuplicatedName(String name) { + if (userRepository.existsByName(name)) { + throw ExceptionStatus.DUPLICATED_NAME.asGreetingException(); + } + } + } diff --git a/v2/backend/greetingCard/src/main/resources/application.yml b/v2/backend/greetingCard/src/main/resources/application.yml index e7fc95d..324745c 100644 --- a/v2/backend/greetingCard/src/main/resources/application.yml +++ b/v2/backend/greetingCard/src/main/resources/application.yml @@ -33,6 +33,7 @@ spring: pageable: default-page-size: 5 + cloud: aws: s3: @@ -50,3 +51,7 @@ logging: level: root: INFO org.springframework: INFO + +server: + servlet: + context-path: /api From 115ac9a2f0feec1037a18c663e1a080d6376c400 Mon Sep 17 00:00:00 2001 From: lamodadite Date: Thu, 23 Jan 2025 16:18:14 +0900 Subject: [PATCH 050/104] =?UTF-8?q?FEAT=20:=20=EB=A9=94=EC=84=B8=EC=A7=80?= =?UTF-8?q?=20=EB=B3=B4=EB=82=B4=EA=B8=B0=20=EC=8B=9C=20=EB=B0=9B=EB=8A=94?= =?UTF-8?q?=20=EC=82=AC=EB=9E=8C=EA=B3=BC=20=EB=B3=B4=EB=82=B4=EB=8A=94=20?= =?UTF-8?q?=EC=82=AC=EB=9E=8C=EC=9D=B4=20=EA=B0=99=EC=9D=84=EB=95=8C=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/cabi/greetingCard/exception/ExceptionStatus.java | 3 ++- .../cabi/greetingCard/message/service/MessageService.java | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionStatus.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionStatus.java index c111b1c..14a118c 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionStatus.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/exception/ExceptionStatus.java @@ -20,7 +20,8 @@ public enum ExceptionStatus { NOT_FOUND_USER(HttpStatus.NOT_FOUND, "존재하지 않는 유저입니디.", "008"), INVALID_QUERYSTRING(HttpStatus.BAD_REQUEST, "잘못된 쿼리스트링입니다.", "009"), UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "권한이 없습니다.", "010"), - NOT_FOUND_MESSAGE(HttpStatus.NOT_FOUND, "존재하지 않는 메세지입니다", "011"); + NOT_FOUND_MESSAGE(HttpStatus.NOT_FOUND, "존재하지 않는 메세지입니다", "011"), + SENDER_EQUAL_RECEIVER(HttpStatus.BAD_REQUEST, "보내는 사람과 받는 사람이 같습니다.", "013"); private final int httpCode; private final String message; diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java index 1011b9e..ac399f9 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java @@ -81,6 +81,7 @@ public void sendMessage(String userName, MessageRequestDto messageData) throws I verifyExistUser(messageData); verifyValidMessageFormat(messageData.getContext()); + verifySenderNotEqualReceiver(userName, messageData); Message message = Message.of(userName, messageData.getReceiverName(), messageData.getContext(), @@ -89,6 +90,12 @@ public void sendMessage(String userName, MessageRequestDto messageData) throws I messageRepository.save(message); } + private void verifySenderNotEqualReceiver(String userName, MessageRequestDto messageData) { + if (userName.equals(messageData.getReceiverName())) { + throw ExceptionStatus.SENDER_EQUAL_RECEIVER.asGreetingException(); + } + } + /** * 로그인한 유저를 제외한 모두에게 메세지를 보냅니다. * From 895f25d0c6c8003891e44b8d016bb5ca6e86cbe4 Mon Sep 17 00:00:00 2001 From: lamodadite Date: Thu, 23 Jan 2025 16:23:34 +0900 Subject: [PATCH 051/104] =?UTF-8?q?REFACTOR=20:=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../message/service/MessageService.java | 1 - .../user/service/UserService.java | 19 ------------------- 2 files changed, 20 deletions(-) diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java index ac399f9..4671168 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java @@ -258,6 +258,5 @@ private void verifyExistUser(MessageRequestDto messageData) { throw ExceptionStatus.NOT_FOUND_USER.asGreetingException(); } } - } diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java index ee47785..22eb210 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java @@ -55,16 +55,13 @@ public void registerUser(String name, String password) { * 진짜 있는 유저일까? 비밀번호는 맞게 입력했을까?, 성공하면 쿠키를 주자! */ public ResponseCookie login(String name, String password) { - // 진짜 있는 유저일까? User loginUser = userRepository.findByName(name) .orElseThrow(ExceptionStatus.LOGIN_FAIL::asGreetingException); - // 비밀번호는 맞게 입력했을까? if (!loginUser.getPassword().equals(password)) { throw ExceptionStatus.LOGIN_FAIL.asGreetingException(); } - // 성공하면 쿠키를 주자! return ResponseCookie.from("userName", name) .maxAge(COOKIE_MAX_AGE) // 유효 기간 1일 .path("/") // 경로 설정 @@ -117,34 +114,18 @@ public void checkAuth(String name) { } } - - /** - * 유저 이름이 영어 + 숫자로만 구성되어 있는지 확인합니다. - * - * @param name - */ private void verifyNameIsNumericOrAlphabet(String name) { if (name == null || !name.matches("^[a-zA-Z0-9]+$")) { throw ExceptionStatus.INVALID_NAME.asGreetingException(); } } - /** - * 유저 이름의 길이가 10자 이내인지 확인합니다 - * - * @param name - */ private void verifyNameLength(String name) { if (name.length() > USER_NAME_LENGTH_LIMIT) { throw ExceptionStatus.INVALID_NAME.asGreetingException(); } } - /** - * 중복된 name 검증 기능 - * - * @param name - */ public void verifyDuplicatedName(String name) { if (userRepository.existsByName(name)) { throw ExceptionStatus.DUPLICATED_NAME.asGreetingException(); From 5212817f804c728895f81e02861144c65351d5b1 Mon Sep 17 00:00:00 2001 From: jiminChoi Date: Fri, 24 Jan 2025 09:57:12 +0900 Subject: [PATCH 052/104] =?UTF-8?q?[FE]=20FEAT:=20=EB=8F=99=EC=A0=81?= =?UTF-8?q?=EA=B2=80=EC=83=89=EA=B8=B0=EB=8A=A5=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/components/ImageUploader.tsx | 87 +++++++++++ .../src/components/SearchInputField.tsx | 147 +++++++++++++++--- v2/frontend/src/pages/SendPage.tsx | 116 ++++++-------- 3 files changed, 263 insertions(+), 87 deletions(-) create mode 100644 v2/frontend/src/components/ImageUploader.tsx diff --git a/v2/frontend/src/components/ImageUploader.tsx b/v2/frontend/src/components/ImageUploader.tsx new file mode 100644 index 0000000..d761cf2 --- /dev/null +++ b/v2/frontend/src/components/ImageUploader.tsx @@ -0,0 +1,87 @@ +import { useRef } from "react"; +import styled from "styled-components"; + +const ImageUploader = ({ + setFile, + file, +}: { + setFile: React.Dispatch>; + file: File | null; +}) => { + const FILE_SIZE_MAX_LIMIT = 30 * 1024 * 1024; // 30MB + const fileInputRef = useRef(null); + + const handleFileChange = (e: React.ChangeEvent) => { + const target = e.target; + const inputFile = e.target.files?.[0]; + if (!inputFile) { + return; + } + + if (!inputFile.name.match(/\.(jpg|jpeg|png)$/)) { + target.value = ""; + alert("이미지 파일만 업로드 가능합니다."); + return; + } + + if (inputFile.size > FILE_SIZE_MAX_LIMIT) { + target.value = ""; + alert("이미지 파일은 30MB를 넘을 수 없습니다."); + return; + } + + setFile(inputFile); + }; + + const handleFileRemove = () => { + setFile(null); + if (fileInputRef.current) { + fileInputRef.current.value = ""; + } + }; + + return ( + + + {file && ( + + x + + )} + + ); +}; + +const FileFormStyled = styled.form` + display: flex; + align-items: center; + justify-content: flex-start; + font-size: 8px; +`; + +const FileInputStyled = styled.input` + font-size: 12px; + &::file-selector-button { + width: 70px; + height: 25px; + border: 1px solid #ccc; + border-radius: 4px; + background: white; + cursor: pointer; + } +`; + +const DeleteButtonStyled = styled.button` + width: 25px; + height: 100%; + padding-bottom: 3px; + color: red; + font-weight: bold; + cursor: pointer; +`; + +export default ImageUploader; diff --git a/v2/frontend/src/components/SearchInputField.tsx b/v2/frontend/src/components/SearchInputField.tsx index b596d18..20bf24d 100644 --- a/v2/frontend/src/components/SearchInputField.tsx +++ b/v2/frontend/src/components/SearchInputField.tsx @@ -1,40 +1,145 @@ -import { useState } from "react"; +import axios from "axios"; +import { useEffect, useState } from "react"; import styled from "styled-components"; +const mockUsers = [ + "kristine", + "insong", + "samin", + "jinhokim", + "inshin", + "minjakim", + "minylee", + "seushin", + "minsikim", + "minjkim2", +]; + +const mockSearch = (searchTerm: string) => { + return mockUsers.filter((user) => user.includes(searchTerm)); +}; + const SearchInputField = ({ - placeHolder, - inputText, + setSearchInputText, }: { - placeHolder: string; - inputText: React.RefObject; + setSearchInputText: (searchTerm: string) => void; }) => { const [isFocused, setIsFocused] = useState(false); + const [searchResult, setSearchResult] = useState([]); + const [inputValue, setInputValue] = useState(""); + + useEffect(() => { + const debounceTimer = setTimeout(() => { + // API call + // if (inputValue.length) { + // try { + // axios.get("/users/search/name?input=" + inputValue) + // .then((response) =>{ + // setSearchResult(response.data); + // }) + // } catch (error: any) { + // console.log(error); + // }} + + if (inputValue.length) setSearchResult(mockSearch(inputValue)); + }, 1000); // 1초 + + return () => { + clearTimeout(debounceTimer); + }; + }, [inputValue]); + + const handleSearch = (e: { target: { value: string } }) => { + if (e.target.value === "") { + setSearchResult([]); + setInputValue(""); + setSearchInputText(""); + } + setInputValue(e.target.value); + setSearchInputText(e.target.value); + }; + + const setSearchName = (value: string) => { + setInputValue(value); + setSearchInputText(value); + setIsFocused(false); + }; return ( - setIsFocused(true)} - onBlur={() => setIsFocused(false)} - // onChange={() => debounce("SlackAlarmSearch", typeSearchInput, 300)} - // onKeyDown={handleInputKey} - /> + <> + + setIsFocused(true)} + onBlur={() => setIsFocused(false)} + $isFocus={isFocused} + /> + {isFocused && ( + + {searchResult.length > 0 && ( + + {searchResult.map((result) => ( + setSearchName(result)} + > + {result} + + ))} + + )} + + )} + + ); }; +const SearchWrapperStyled = styled.div` + width: 100%; + position: relative; +`; + const SearchInputFieldStyled = styled.input<{ $isFocus: boolean }>` width: 100%; height: 40px; - background-color: var(--ref-white); + background-color: #ffffff; border-radius: 8px; - border: 2px solid - ${({ $isFocus }) => ($isFocus ? "var(--ref-purple-500)" : "transparent")}; text-align: left; - padding: 0 10px; - ::placeholder { - color: var(--ref-gray-400); + padding: 10px; + border-radius: 8px; + border: 2px solid ${({ $isFocus }) => ($isFocus ? "#9747ff" : "#ffffff")}; + text-align: left; +`; + +const SearchResultStyled = styled.div` + width: 100%; + position: absolute; + z-index: 1000; + border-radius: 8px; + background-color: #ffffff; +`; + +const SearchUlStyled = styled.ul` + min-height: 30px; + padding-left: 0px; +`; + +const SearchLiStyled = styled.li` + height: 30px; + display: flex; + justify-content: flex-start; + align-items: center; + padding-left: 10px; + padding-bottom: 3px; + border-radius: 8px; + cursor: pointer; + &:hover { + background-color: #9747ff; + color: #ffffff; } `; diff --git a/v2/frontend/src/pages/SendPage.tsx b/v2/frontend/src/pages/SendPage.tsx index c6866fc..034a22e 100644 --- a/v2/frontend/src/pages/SendPage.tsx +++ b/v2/frontend/src/pages/SendPage.tsx @@ -1,29 +1,51 @@ import styled from "styled-components"; import { useRef, useState } from "react"; import SearchInputField from "../components/SearchInputField"; +import { Link } from "react-router"; +import axios from "axios"; +import ImageUploader from "../components/ImageUploader"; + const SendPage = () => { - const idSearchInputRef = useRef(null); + const [searchInputText, setSearchInputText] = useState(""); const messageTextAreaRef = useRef(null); const [isFocused, setIsFocused] = useState(false); + const [file, setFile] = useState(null); + + const handleSubmit = async () => { + const formData = new FormData(); + + formData.append("receiverName", searchInputText); + formData.append("context", messageTextAreaRef.current?.value || ""); + if (file) { + formData.append("image", file); + } + + try { + const response = await axios.post("/messages", formData, { + headers: { + "Content-Type": "multipart/form-data", + }, + }); + alert("메시지가 성공적으로 전송되었습니다."); + } catch (error) { + alert(error); + } + }; return ( - 알림 + + 덕담 보러 가기 + + 덕담 보내기 - + 받는이 ( Intra ID / @everyone ) * - - {/* */} + @@ -42,11 +64,15 @@ const SendPage = () => { 사진 ( jpg, jpeg, png ) + - 보내기 + 보내기 - + ); @@ -56,7 +82,6 @@ export default SendPage; const WrapperStyled = styled.div` font-family: "Noto Sans KR", sans-serif; - /* height: 100%; */ height: 500px; display: flex; flex-direction: column; @@ -65,6 +90,15 @@ const WrapperStyled = styled.div` padding: 60px 0; `; +const LinkWrapperStyled = styled.div` + width: 80%; + height: 50px; + display: flex; + justify-content: flex-end; + color: #9747ff; + font-size: 0.875rem; +`; + const TitleContainerStyled = styled.div` width: 80%; max-width: 1000px; @@ -85,35 +119,7 @@ const ContainerStyled = styled.div` margin-bottom: 40px; `; -const SubTitleStyled = styled.h2` - font-size: 1.25rem; - font-weight: 700; - margin-bottom: 20px; -`; - -const CapsuleWrappingStyled = styled.div` - width: 100%; - display: flex; - align-items: center; - gap: 10px; - flex-wrap: wrap; -`; - -const CapsuleButtonStyled = styled.span<{ - channelBtnIsClicked?: boolean; - templateBtnIsClicked?: boolean; -}>` - display: flex; - justify-content: center; - align-items: center; - padding: 8px 20px; - background: #f5f5f5; - border: 1px solid #ffffff; - border-radius: 22px; - cursor: pointer; -`; - -const FormWappingStyled = styled.div` +const FormWrapperStyled = styled.div` display: flex; flex-direction: column; justify-content: flex-start; @@ -152,26 +158,6 @@ const SendTextFieldStyled = styled.textarea<{ $isFocus: boolean }>` } `; -const FormTextareaStyled = styled.textarea` - color: var(--normal-text-color); - box-sizing: border-box; - width: 100%; - min-height: 200px; - background-color: #3d3f40; - border-radius: 8px; - border: 1px solid #ffffff; - resize: none; - outline: none; - :focus { - border: 1px solid #9747ff; - } - text-align: left; - padding: 10px; - ::placeholder { - color: var(--line-color); - } -`; - const FormButtonContainerStyled = styled.div` width: 100%; display: flex; @@ -189,7 +175,5 @@ const FormButtonStyled = styled.button` border: 1px solid #ffffff; border-radius: 4px; cursor: pointer; - /* :hover { - opacity: 0.85; - } */ + `; From b93cf1f2f98a0c0d2ce8f74e42901e3890b1ca1f Mon Sep 17 00:00:00 2001 From: jiminChoi Date: Fri, 24 Jan 2025 10:07:24 +0900 Subject: [PATCH 053/104] =?UTF-8?q?[FE]=20FIX:=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EC=82=AC=EC=9D=B4=EC=A6=88=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/components/ImageUploader.tsx | 3 ++- v2/frontend/src/pages/SendPage.tsx | 13 ++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/v2/frontend/src/components/ImageUploader.tsx b/v2/frontend/src/components/ImageUploader.tsx index d761cf2..5b7e3ef 100644 --- a/v2/frontend/src/components/ImageUploader.tsx +++ b/v2/frontend/src/components/ImageUploader.tsx @@ -80,7 +80,8 @@ const DeleteButtonStyled = styled.button` height: 100%; padding-bottom: 3px; color: red; - font-weight: bold; + font-size: 14px; + font-weight: 500; cursor: pointer; `; diff --git a/v2/frontend/src/pages/SendPage.tsx b/v2/frontend/src/pages/SendPage.tsx index 034a22e..af1dc47 100644 --- a/v2/frontend/src/pages/SendPage.tsx +++ b/v2/frontend/src/pages/SendPage.tsx @@ -5,7 +5,6 @@ import { Link } from "react-router"; import axios from "axios"; import ImageUploader from "../components/ImageUploader"; - const SendPage = () => { const [searchInputText, setSearchInputText] = useState(""); const messageTextAreaRef = useRef(null); @@ -64,10 +63,7 @@ const SendPage = () => { 사진 ( jpg, jpeg, png ) - + 보내기 @@ -165,9 +161,9 @@ const FormButtonContainerStyled = styled.div` `; const FormButtonStyled = styled.button` - width: auto; - height: auto; - padding: 10px 16px; + width: 100px; + height: 30px; + /* padding: 10px 16px; */ font-size: 0.875rem; background-color: #9747ff; color: #ffffff; @@ -175,5 +171,4 @@ const FormButtonStyled = styled.button` border: 1px solid #ffffff; border-radius: 4px; cursor: pointer; - `; From b05b5d07be89e46041b394cd107e28ab1a03af32 Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Fri, 24 Jan 2025 11:36:01 +0900 Subject: [PATCH 054/104] =?UTF-8?q?[FE]=20FEAT:=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/pages/LoginPage.tsx | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/v2/frontend/src/pages/LoginPage.tsx b/v2/frontend/src/pages/LoginPage.tsx index 3cf111e..edea12d 100644 --- a/v2/frontend/src/pages/LoginPage.tsx +++ b/v2/frontend/src/pages/LoginPage.tsx @@ -3,11 +3,33 @@ import { Link } from "react-router-dom"; import styled from "styled-components"; import UserInputField from "../components/UserInputField"; import { ReactComponent as NewYearImg } from "../assets/images/newYear.svg"; +import { login } from "../api/users"; const LoginPage = () => { const [id, setId] = useState(""); const [pw, setPw] = useState(""); + const handleLogin = async () => { + const idRegex = /^[A-Za-z0-9]{1,10}$/; + const pwRegex = /^[A-Za-z0-9]+$/; + const fourInARowRegex = /(.)\1{3}/; + + const isValid = + idRegex.test(id) && pwRegex.test(pw) && !fourInARowRegex.test(pw); + + if (!isValid) { + alert("로그인 실패"); + return; + } + + try { + const data = { name: id, password: pw }; + const response = await login(data); + } catch (error) { + // TODO: 에러 처리 + } + }; + return ( @@ -23,7 +45,7 @@ const LoginPage = () => { placeholder="pw" /> 회원가입 - 로그인 + 로그인 ); }; From bac45de5505e1912793bbeaa3685ef75ea338864 Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Fri, 24 Jan 2025 11:39:23 +0900 Subject: [PATCH 055/104] =?UTF-8?q?[FE]=20FEAT:=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80?= =?UTF-8?q?=EC=82=AC=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/pages/RegisterPage.tsx | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/v2/frontend/src/pages/RegisterPage.tsx b/v2/frontend/src/pages/RegisterPage.tsx index fdc625d..933c02e 100644 --- a/v2/frontend/src/pages/RegisterPage.tsx +++ b/v2/frontend/src/pages/RegisterPage.tsx @@ -2,11 +2,32 @@ import { useState } from "react"; import { ReactComponent as NewYearImg } from "../assets/images/newYear.svg"; import styled from "styled-components"; import UserInputField from "../components/UserInputField"; +import { register } from "../api/users"; const RegisterPage = () => { const [id, setId] = useState(""); const [pw, setPw] = useState(""); + const handleRegister = async () => { + const idRegex = /^[A-Za-z0-9]{1,10}$/; + const pwRegex = /^[A-Za-z0-9]+$/; + const fourInARowRegex = /(.)\1{3}/; + + const isValid = + idRegex.test(id) && pwRegex.test(pw) && !fourInARowRegex.test(pw); + + if (!isValid) { + alert("로그인 실패"); + return; + } + + try { + const data = { name: id, password: pw }; + const response = await register(data); + } catch (error) { + // TODO: 에러 처리 + } + }; return ( @@ -22,7 +43,9 @@ const RegisterPage = () => { placeholder="pw" /> 비밀번호는 변경할 수 없습니다 - 회원가입 + + 회원가입 + ); }; From cd3b02b0640673283af827e62d9489bb05d6158b Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Fri, 24 Jan 2025 11:57:16 +0900 Subject: [PATCH 056/104] =?UTF-8?q?[FE]=20FEAT:=20=EB=B9=84=EB=B0=80?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EC=9E=85=EB=A0=A5=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?password=20=EC=86=8D=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/components/UserInputField.tsx | 3 +++ v2/frontend/src/pages/LoginPage.tsx | 2 ++ v2/frontend/src/pages/RegisterPage.tsx | 2 ++ 3 files changed, 7 insertions(+) diff --git a/v2/frontend/src/components/UserInputField.tsx b/v2/frontend/src/components/UserInputField.tsx index 747d488..6faeed0 100644 --- a/v2/frontend/src/components/UserInputField.tsx +++ b/v2/frontend/src/components/UserInputField.tsx @@ -2,12 +2,14 @@ import React, { useState } from "react"; import styled from "styled-components"; type LoginInputFieldProps = { + type?: string; value: string; onChange: (e: React.ChangeEvent) => void; placeholder?: string; }; const LoginInputField: React.FC = ({ + type, value, onChange, placeholder, @@ -17,6 +19,7 @@ const LoginInputField: React.FC = ({ return ( { CABI-Onboarding setId(e.target.value)} placeholder="id" /> setPw(e.target.value)} placeholder="pw" diff --git a/v2/frontend/src/pages/RegisterPage.tsx b/v2/frontend/src/pages/RegisterPage.tsx index 933c02e..464e417 100644 --- a/v2/frontend/src/pages/RegisterPage.tsx +++ b/v2/frontend/src/pages/RegisterPage.tsx @@ -33,11 +33,13 @@ const RegisterPage = () => { CABI-Onboarding setId(e.target.value)} placeholder="id" /> setPw(e.target.value)} placeholder="pw" From 755effd124f3aab334eb6ab7201b51d5ec5577af Mon Sep 17 00:00:00 2001 From: jiminChoi Date: Fri, 24 Jan 2025 12:07:41 +0900 Subject: [PATCH 057/104] =?UTF-8?q?[FE]=20FIX:=20=EA=B2=80=EC=83=89=20api?= =?UTF-8?q?=20=EC=9A=94=EC=B2=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/SearchInputField.tsx | 64 +++++++++++-------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/v2/frontend/src/components/SearchInputField.tsx b/v2/frontend/src/components/SearchInputField.tsx index 20bf24d..57a883e 100644 --- a/v2/frontend/src/components/SearchInputField.tsx +++ b/v2/frontend/src/components/SearchInputField.tsx @@ -1,23 +1,25 @@ import axios from "axios"; import { useEffect, useState } from "react"; import styled from "styled-components"; +import { searchGroup, searchName } from "../api/users"; +import { al } from "react-router/dist/development/fog-of-war-DLtn2OLr"; -const mockUsers = [ - "kristine", - "insong", - "samin", - "jinhokim", - "inshin", - "minjakim", - "minylee", - "seushin", - "minsikim", - "minjkim2", -]; - -const mockSearch = (searchTerm: string) => { - return mockUsers.filter((user) => user.includes(searchTerm)); -}; +// const mockUsers = [ +// "kristine", +// "insong", +// "samin", +// "jinhokim", +// "inshin", +// "minjakim", +// "minylee", +// "seushin", +// "minsikim", +// "minjkim2", +// ]; + +// const mockSearch = (searchTerm: string) => { +// return mockUsers.filter((user) => user.includes(searchTerm)); +// }; const SearchInputField = ({ setSearchInputText, @@ -29,19 +31,25 @@ const SearchInputField = ({ const [inputValue, setInputValue] = useState(""); useEffect(() => { - const debounceTimer = setTimeout(() => { + const debounceTimer = setTimeout(async () => { // API call - // if (inputValue.length) { - // try { - // axios.get("/users/search/name?input=" + inputValue) - // .then((response) =>{ - // setSearchResult(response.data); - // }) - // } catch (error: any) { - // console.log(error); - // }} - - if (inputValue.length) setSearchResult(mockSearch(inputValue)); + if(inputValue == "@everyone"){ + try { + const res = await searchGroup({ input: inputValue }); + setSearchResult(res.data); + } catch (error: any) { + alert(error.response.data.message); + }} + + if (inputValue.length) { + try { + const res = await searchName({ input: inputValue }); + setSearchResult(res.data); + } catch (error: any) { + alert(error.response.data.message); + }} + + // if (inputValue.length) setSearchResult(mockSearch(inputValue)); }, 1000); // 1초 return () => { From 3586ec7aee1f5b9d8f0c6fde7758b32dd55dc156 Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Fri, 24 Jan 2025 12:13:57 +0900 Subject: [PATCH 058/104] =?UTF-8?q?[FE]=20FIX:=20Chrome=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=EC=99=84=EC=84=B1=EC=9C=BC=EB=A1=9C=20=EC=9D=B8?= =?UTF-8?q?=ED=95=9C=20Warning=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/pages/LoginPage.tsx | 26 ++++++++++++++------------ v2/frontend/src/pages/RegisterPage.tsx | 26 ++++++++++++++------------ 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/v2/frontend/src/pages/LoginPage.tsx b/v2/frontend/src/pages/LoginPage.tsx index 2176ec0..f2f84da 100644 --- a/v2/frontend/src/pages/LoginPage.tsx +++ b/v2/frontend/src/pages/LoginPage.tsx @@ -34,18 +34,20 @@ const LoginPage = () => { CABI-Onboarding - setId(e.target.value)} - placeholder="id" - /> - setPw(e.target.value)} - placeholder="pw" - /> +

+ setId(e.target.value)} + placeholder="id" + /> + setPw(e.target.value)} + placeholder="pw" + /> + 회원가입 로그인 diff --git a/v2/frontend/src/pages/RegisterPage.tsx b/v2/frontend/src/pages/RegisterPage.tsx index 464e417..b3b86e4 100644 --- a/v2/frontend/src/pages/RegisterPage.tsx +++ b/v2/frontend/src/pages/RegisterPage.tsx @@ -32,18 +32,20 @@ const RegisterPage = () => { CABI-Onboarding - setId(e.target.value)} - placeholder="id" - /> - setPw(e.target.value)} - placeholder="pw" - /> +
+ setId(e.target.value)} + placeholder="id" + /> + setPw(e.target.value)} + placeholder="pw" + /> + 비밀번호는 변경할 수 없습니다 회원가입 From e44e136ddeaad41f133efa652f3a95802492baf1 Mon Sep 17 00:00:00 2001 From: lamodadite Date: Fri, 24 Jan 2025 12:17:47 +0900 Subject: [PATCH 059/104] =?UTF-8?q?REFACTOR=20:=20docker-compose=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EC=97=90=20.env=20=EC=9D=B8=EC=8B=9D?= =?UTF-8?q?=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/backend/greetingCard/docker-compose.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/v2/backend/greetingCard/docker-compose.yml b/v2/backend/greetingCard/docker-compose.yml index 3232fc1..bf582dd 100644 --- a/v2/backend/greetingCard/docker-compose.yml +++ b/v2/backend/greetingCard/docker-compose.yml @@ -7,6 +7,8 @@ services: dockerfile: Dockerfile ports: - "8080:8080" + env_file: + - .env environment: SPRING_DATASOURCE_URL: ${DB_URL} SPRING_DATASOURCE_USERNAME: ${DB_USERNAME} @@ -22,6 +24,8 @@ services: db: image: mariadb:10.5 restart: always + env_file: + - .env environment: MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} MYSQL_DATABASE: greetingdb From 81647d032d36d5e0166d1ce0bbeb1365f9584891 Mon Sep 17 00:00:00 2001 From: Jihyun Kim Date: Fri, 24 Jan 2025 12:21:40 +0900 Subject: [PATCH 060/104] =?UTF-8?q?[FE]=20FIX:=20BE=20=ED=8F=AC=ED=8A=B8?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/api/axios.custom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/frontend/src/api/axios.custom.ts b/v2/frontend/src/api/axios.custom.ts index 95311d4..64633c2 100644 --- a/v2/frontend/src/api/axios.custom.ts +++ b/v2/frontend/src/api/axios.custom.ts @@ -1,7 +1,7 @@ import axios from "axios"; const axiosInstance = axios.create({ - baseURL: "http://localhost:3000/api", // 기본 URL + baseURL: "http://localhost:8080/api", // 기본 URL headers: { "Content-Type": "application/json", }, From a5855c216a0168876ee24f843fd7a256ceb75811 Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Fri, 24 Jan 2025 12:28:53 +0900 Subject: [PATCH 061/104] =?UTF-8?q?[FE]=20FIX:=20=EB=B9=84=EB=B0=80?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=ED=95=84=EB=93=9C=20type=20=EC=9D=84=20pa?= =?UTF-8?q?ssword=20=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/components/UserInputField.tsx | 3 +++ v2/frontend/src/pages/LoginPage.tsx | 4 +++- v2/frontend/src/pages/RegisterPage.tsx | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/v2/frontend/src/components/UserInputField.tsx b/v2/frontend/src/components/UserInputField.tsx index 6faeed0..5c3221b 100644 --- a/v2/frontend/src/components/UserInputField.tsx +++ b/v2/frontend/src/components/UserInputField.tsx @@ -3,6 +3,7 @@ import styled from "styled-components"; type LoginInputFieldProps = { type?: string; + autocomplete?: string; value: string; onChange: (e: React.ChangeEvent) => void; placeholder?: string; @@ -10,6 +11,7 @@ type LoginInputFieldProps = { const LoginInputField: React.FC = ({ type, + autocomplete, value, onChange, placeholder, @@ -21,6 +23,7 @@ const LoginInputField: React.FC = ({ { setId(e.target.value)} placeholder="id" /> setPw(e.target.value)} placeholder="pw" /> diff --git a/v2/frontend/src/pages/RegisterPage.tsx b/v2/frontend/src/pages/RegisterPage.tsx index b3b86e4..f9d3657 100644 --- a/v2/frontend/src/pages/RegisterPage.tsx +++ b/v2/frontend/src/pages/RegisterPage.tsx @@ -36,12 +36,14 @@ const RegisterPage = () => { setId(e.target.value)} placeholder="id" /> setPw(e.target.value)} placeholder="pw" /> From 2e1dcc334467a7fc872a2b36ab40564f0105e6f4 Mon Sep 17 00:00:00 2001 From: lamodadite Date: Fri, 24 Jan 2025 12:44:40 +0900 Subject: [PATCH 062/104] =?UTF-8?q?FIX=20:=20=EB=A9=94=EC=84=B8=EC=A7=80?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=ED=95=A0=EB=95=8C=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=8B=9C=EC=9E=91=201=20->=200=20=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD,=20=EC=9D=91=EB=8B=B5=EC=97=90?= =?UTF-8?q?=20currentPage=20=EC=B6=94=EA=B0=80,=20application.yml=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=EB=B2=A0=EC=9D=B4=EC=8A=A4=20rds?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/MessageResponsePaginationDto.java | 1 + .../message/service/MessageService.java | 5 +- .../src/main/resources/application.yml | 54 +++++++++---------- 3 files changed, 28 insertions(+), 32 deletions(-) diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/dto/MessageResponsePaginationDto.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/dto/MessageResponsePaginationDto.java index eddb07f..3a139f4 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/dto/MessageResponsePaginationDto.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/dto/MessageResponsePaginationDto.java @@ -10,4 +10,5 @@ public class MessageResponsePaginationDto { private List messages; private int totalLength; + private int currentPage; } diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java index 4671168..fdfe170 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java @@ -183,8 +183,7 @@ public MessageResponsePaginationDto getMessages(String userName, Pageable pageab int category) { verifyValidPageInfo(pageable); - // pageNumber는 프론트에서 1부터 인덱싱하기 때문에 -1을 해주고 있음 - PageRequest pageRequest = PageRequest.of(pageable.getPageNumber() - 1, + PageRequest pageRequest = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.by("created").descending()); @@ -195,7 +194,7 @@ public MessageResponsePaginationDto getMessages(String userName, Pageable pageab userName.equals(message.getSenderName()))).toList(); return new MessageResponsePaginationDto(messageResponseDtoList, - messageList.getTotalPages()); + messageList.getTotalPages(), pageable.getPageNumber()); } /** diff --git a/v2/backend/greetingCard/src/main/resources/application.yml b/v2/backend/greetingCard/src/main/resources/application.yml index 324745c..c6aa270 100644 --- a/v2/backend/greetingCard/src/main/resources/application.yml +++ b/v2/backend/greetingCard/src/main/resources/application.yml @@ -1,37 +1,33 @@ -#spring: -# datasource: -# driver-class-name: org.mariadb.jdbc.Driver -# url: ${DB_URL} -# username: ${DB_USERNAME} -# password: ${DB_PASSWORD} -# jpa: -# hibernate: -# ddl-auto: update -# servlet: -# multipart: -# max-file-size: 30MB -# max-request-size: 30MB -# config: -# import: application.properties - spring: datasource: - driver-class-name: org.h2.Driver - url: jdbc:h2:tcp://localhost/~/onboarding - username: root - password: 1234 - + driver-class-name: org.mariadb.jdbc.Driver + url: ${DB_URL} + username: ${DB_USERNAME} + password: ${DB_PASSWORD} jpa: hibernate: ddl-auto: update - properties: - hibernate: - show_sql: true #콘솔에 로그가 나옴 - format_sql: true #이쁘게 해줌 - data: - web: - pageable: - default-page-size: 5 + servlet: + multipart: + max-file-size: 30MB + max-request-size: 30MB + config: + import: application.properties + +#spring: +# datasource: +# driver-class-name: org.h2.Driver +# url: jdbc:h2:tcp://localhost/~/onboarding +# username: root +# password: 1234 +# +# jpa: +# hibernate: +# ddl-auto: update +# properties: +# hibernate: +# show_sql: true #콘솔에 로그가 나옴 +# format_sql: true #이쁘게 해줌 cloud: From 496b8b794e826259cc7ce8de514e3c4e7da575e6 Mon Sep 17 00:00:00 2001 From: jiminChoi Date: Fri, 24 Jan 2025 12:45:35 +0900 Subject: [PATCH 063/104] =?UTF-8?q?[FE]=20FIX:=20=EA=B2=80=EC=83=89=20api?= =?UTF-8?q?=20=EC=9A=94=EC=B2=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/api/users.ts | 4 +-- .../src/components/SearchInputField.tsx | 30 ++++++------------- 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/v2/frontend/src/api/users.ts b/v2/frontend/src/api/users.ts index 86c35d8..6187b39 100644 --- a/v2/frontend/src/api/users.ts +++ b/v2/frontend/src/api/users.ts @@ -13,9 +13,9 @@ export const register = async (data: object) => { }; export const searchGroup = async (params: object) => { - return service.get(`/users/search/groups`, { params }); + return service.get(`/users/search/group`, { params }); }; export const searchName = async (params: object) => { - return service.get(`/users/search/names`, { params }); + return service.get(`/users/search/name`, { params }); }; diff --git a/v2/frontend/src/components/SearchInputField.tsx b/v2/frontend/src/components/SearchInputField.tsx index 57a883e..1456dd3 100644 --- a/v2/frontend/src/components/SearchInputField.tsx +++ b/v2/frontend/src/components/SearchInputField.tsx @@ -4,22 +4,6 @@ import styled from "styled-components"; import { searchGroup, searchName } from "../api/users"; import { al } from "react-router/dist/development/fog-of-war-DLtn2OLr"; -// const mockUsers = [ -// "kristine", -// "insong", -// "samin", -// "jinhokim", -// "inshin", -// "minjakim", -// "minylee", -// "seushin", -// "minsikim", -// "minjkim2", -// ]; - -// const mockSearch = (searchTerm: string) => { -// return mockUsers.filter((user) => user.includes(searchTerm)); -// }; const SearchInputField = ({ setSearchInputText, @@ -33,23 +17,27 @@ const SearchInputField = ({ useEffect(() => { const debounceTimer = setTimeout(async () => { // API call - if(inputValue == "@everyone"){ + + if (inputValue == "") return; + if (inputValue[0] === "@") { try { const res = await searchGroup({ input: inputValue }); + console.log("res : ", res.data); setSearchResult(res.data); } catch (error: any) { alert(error.response.data.message); - }} + } + } if (inputValue.length) { try { const res = await searchName({ input: inputValue }); setSearchResult(res.data); } catch (error: any) { - alert(error.response.data.message); - }} + // alert(error.response.data.message); + } + } - // if (inputValue.length) setSearchResult(mockSearch(inputValue)); }, 1000); // 1초 return () => { From ba2359ca0fd0e7cedd6e97725aed8b5b2f5bf86f Mon Sep 17 00:00:00 2001 From: lamodadite Date: Fri, 24 Jan 2025 13:08:38 +0900 Subject: [PATCH 064/104] =?UTF-8?q?FIX=20:=20dockerfile=20jdk=20=EB=B2=84?= =?UTF-8?q?=EC=A0=84=2017=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/backend/greetingCard/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/backend/greetingCard/Dockerfile b/v2/backend/greetingCard/Dockerfile index 5d26185..573054d 100644 --- a/v2/backend/greetingCard/Dockerfile +++ b/v2/backend/greetingCard/Dockerfile @@ -1,5 +1,5 @@ # Stage 1: Build the application using Gradle Wrapper -FROM openjdk:21-jdk-slim AS build +FROM openjdk:17-jdk-slim AS build # Install dependencies for building RUN apt-get update && apt-get install -y wget unzip && rm -rf /var/lib/apt/lists/* From 18d3dc7061a432bccd367236fbe08eb15066c8c6 Mon Sep 17 00:00:00 2001 From: JunSeong Date: Fri, 24 Jan 2025 13:32:30 +0900 Subject: [PATCH 065/104] =?UTF-8?q?[FE]=20FIX:=20send=20message=20header?= =?UTF-8?q?=EB=A5=BC=20multipart=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/api/axios.custom.ts | 4 ++-- v2/frontend/src/api/messages.ts | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/v2/frontend/src/api/axios.custom.ts b/v2/frontend/src/api/axios.custom.ts index 64633c2..9adec74 100644 --- a/v2/frontend/src/api/axios.custom.ts +++ b/v2/frontend/src/api/axios.custom.ts @@ -9,8 +9,8 @@ const axiosInstance = axios.create({ }); export const service = { - post: async (url: string, data: object) => { - return axiosInstance.post(url, data); + post: async (url: string, data: object, config?: object) => { + return axiosInstance.post(url, data, config); }, get: async (url: string, config?: { params?: object }) => { diff --git a/v2/frontend/src/api/messages.ts b/v2/frontend/src/api/messages.ts index d05b0cf..a8bbddd 100644 --- a/v2/frontend/src/api/messages.ts +++ b/v2/frontend/src/api/messages.ts @@ -1,7 +1,11 @@ import { service } from "./axios.custom"; export const sendMessage = async (data: object) => { - return service.post(`/messages`, data); + return service.post(`/messages`, data, { + headers: { + "Content-Type": "multipart/form-data", + }, + }); }; export const getMessages = async (params: object) => { From 5e9eee06a023773e36baf528a25ebbe72110b23a Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Fri, 24 Jan 2025 14:36:07 +0900 Subject: [PATCH 066/104] =?UTF-8?q?[FE]=20FEAT:=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EC=84=B1=EA=B3=B5=EC=8B=9C=20=EB=8D=95=EB=8B=B4=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=A1=9C=EB=A1=9C=20navigate=20=ED=95=98=EB=8A=94=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/pages/LoginPage.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/v2/frontend/src/pages/LoginPage.tsx b/v2/frontend/src/pages/LoginPage.tsx index 8b61451..5888a64 100644 --- a/v2/frontend/src/pages/LoginPage.tsx +++ b/v2/frontend/src/pages/LoginPage.tsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import { Link } from "react-router-dom"; +import { Link, useNavigate } from "react-router-dom"; import styled from "styled-components"; import UserInputField from "../components/UserInputField"; import { ReactComponent as NewYearImg } from "../assets/images/newYear.svg"; @@ -8,6 +8,7 @@ import { login } from "../api/users"; const LoginPage = () => { const [id, setId] = useState(""); const [pw, setPw] = useState(""); + const navigate = useNavigate(); const handleLogin = async () => { const idRegex = /^[A-Za-z0-9]{1,10}$/; @@ -25,6 +26,9 @@ const LoginPage = () => { try { const data = { name: id, password: pw }; const response = await login(data); + if (response.status === 200) { + navigate("/list"); + } } catch (error) { // TODO: 에러 처리 } From 507aab22d5ece3055d37fa835428ef9b91d4dde1 Mon Sep 17 00:00:00 2001 From: jiminChoi Date: Fri, 24 Jan 2025 14:37:13 +0900 Subject: [PATCH 067/104] =?UTF-8?q?[FE]=20FIX:=20=EA=B2=80=EC=83=89=20api?= =?UTF-8?q?=20=EC=9A=94=EC=B2=AD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/SearchInputField.tsx | 52 +++++++------------ v2/frontend/src/pages/SendPage.tsx | 6 +-- 2 files changed, 21 insertions(+), 37 deletions(-) diff --git a/v2/frontend/src/components/SearchInputField.tsx b/v2/frontend/src/components/SearchInputField.tsx index 1456dd3..b5a22fa 100644 --- a/v2/frontend/src/components/SearchInputField.tsx +++ b/v2/frontend/src/components/SearchInputField.tsx @@ -1,9 +1,6 @@ -import axios from "axios"; import { useEffect, useState } from "react"; import styled from "styled-components"; import { searchGroup, searchName } from "../api/users"; -import { al } from "react-router/dist/development/fog-of-war-DLtn2OLr"; - const SearchInputField = ({ setSearchInputText, @@ -11,48 +8,39 @@ const SearchInputField = ({ setSearchInputText: (searchTerm: string) => void; }) => { const [isFocused, setIsFocused] = useState(false); - const [searchResult, setSearchResult] = useState([]); + const [searchResult, setSearchResult] = useState<{ names: string[] }>({ + names: [], + }); const [inputValue, setInputValue] = useState(""); useEffect(() => { const debounceTimer = setTimeout(async () => { - // API call - - if (inputValue == "") return; - if (inputValue[0] === "@") { - try { + if (inputValue == "") { + setSearchResult({ names: [] }); + return; + } + try { + if (inputValue[0] === "@") { const res = await searchGroup({ input: inputValue }); - console.log("res : ", res.data); setSearchResult(res.data); - } catch (error: any) { - alert(error.response.data.message); - } - } - - if (inputValue.length) { - try { + } else { const res = await searchName({ input: inputValue }); setSearchResult(res.data); - } catch (error: any) { - // alert(error.response.data.message); } + } catch (error: any) { + alert(error.response.data.message); + setSearchResult({ names: [] }); } - - }, 1000); // 1초 + }, 500); return () => { clearTimeout(debounceTimer); }; }, [inputValue]); - const handleSearch = (e: { target: { value: string } }) => { - if (e.target.value === "") { - setSearchResult([]); - setInputValue(""); - setSearchInputText(""); - } - setInputValue(e.target.value); - setSearchInputText(e.target.value); + const handleSearch = async (e: { target: { value: string } }) => { + const value = e.target.value; + setInputValue(value); }; const setSearchName = (value: string) => { @@ -70,14 +58,14 @@ const SearchInputField = ({ placeholder="intra id / @everyone" onChange={handleSearch} onFocus={() => setIsFocused(true)} - onBlur={() => setIsFocused(false)} + onBlur={() => setTimeout(() => setIsFocused(false), 200)} $isFocus={isFocused} /> {isFocused && ( - {searchResult.length > 0 && ( + {searchResult.names?.length > 0 && ( - {searchResult.map((result) => ( + {searchResult.names.map((result) => ( setSearchName(result)} diff --git a/v2/frontend/src/pages/SendPage.tsx b/v2/frontend/src/pages/SendPage.tsx index af1dc47..3b40596 100644 --- a/v2/frontend/src/pages/SendPage.tsx +++ b/v2/frontend/src/pages/SendPage.tsx @@ -21,11 +21,7 @@ const SendPage = () => { } try { - const response = await axios.post("/messages", formData, { - headers: { - "Content-Type": "multipart/form-data", - }, - }); + const response = await axios.post("/messages", formData); alert("메시지가 성공적으로 전송되었습니다."); } catch (error) { alert(error); From 429ebff34b0abb4cdfe37425affaf9e519bcef82 Mon Sep 17 00:00:00 2001 From: jiminChoi Date: Fri, 24 Jan 2025 14:53:32 +0900 Subject: [PATCH 068/104] =?UTF-8?q?[FE]=20FIX:=20=EB=B3=B4=EB=82=B4?= =?UTF-8?q?=EA=B8=B0=20api=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/pages/SendPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v2/frontend/src/pages/SendPage.tsx b/v2/frontend/src/pages/SendPage.tsx index 3b40596..39b2e56 100644 --- a/v2/frontend/src/pages/SendPage.tsx +++ b/v2/frontend/src/pages/SendPage.tsx @@ -4,6 +4,7 @@ import SearchInputField from "../components/SearchInputField"; import { Link } from "react-router"; import axios from "axios"; import ImageUploader from "../components/ImageUploader"; +import { sendMessage } from "../api/messages"; const SendPage = () => { const [searchInputText, setSearchInputText] = useState(""); @@ -21,7 +22,7 @@ const SendPage = () => { } try { - const response = await axios.post("/messages", formData); + const response = await sendMessage({data: formData}); alert("메시지가 성공적으로 전송되었습니다."); } catch (error) { alert(error); @@ -159,7 +160,6 @@ const FormButtonContainerStyled = styled.div` const FormButtonStyled = styled.button` width: 100px; height: 30px; - /* padding: 10px 16px; */ font-size: 0.875rem; background-color: #9747ff; color: #ffffff; From 6c91ee672b95aa27dcc5b124bef15d632e65b383 Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Fri, 24 Jan 2025 14:56:29 +0900 Subject: [PATCH 069/104] =?UTF-8?q?[FE]=20FEAT:=20=EC=97=90=EB=9F=AC=20ale?= =?UTF-8?q?rt=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/pages/LoginPage.tsx | 4 ++-- v2/frontend/src/pages/RegisterPage.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/v2/frontend/src/pages/LoginPage.tsx b/v2/frontend/src/pages/LoginPage.tsx index 5888a64..09f7bd2 100644 --- a/v2/frontend/src/pages/LoginPage.tsx +++ b/v2/frontend/src/pages/LoginPage.tsx @@ -29,8 +29,8 @@ const LoginPage = () => { if (response.status === 200) { navigate("/list"); } - } catch (error) { - // TODO: 에러 처리 + } catch (error: any) { + alert(error.response.data.message); } }; diff --git a/v2/frontend/src/pages/RegisterPage.tsx b/v2/frontend/src/pages/RegisterPage.tsx index f9d3657..5acb812 100644 --- a/v2/frontend/src/pages/RegisterPage.tsx +++ b/v2/frontend/src/pages/RegisterPage.tsx @@ -24,8 +24,8 @@ const RegisterPage = () => { try { const data = { name: id, password: pw }; const response = await register(data); - } catch (error) { - // TODO: 에러 처리 + } catch (error: any) { + alert(error.response.data.message); } }; return ( From d9b174b6064329793081ba1b2197b6a6e620fb5b Mon Sep 17 00:00:00 2001 From: jiminChoi Date: Fri, 24 Jan 2025 15:47:17 +0900 Subject: [PATCH 070/104] =?UTF-8?q?[FE]=20FIX:=20=EB=B3=B4=EB=82=B4?= =?UTF-8?q?=EA=B8=B0=20api=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/components/SearchInputField.tsx | 1 + v2/frontend/src/pages/SendPage.tsx | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/v2/frontend/src/components/SearchInputField.tsx b/v2/frontend/src/components/SearchInputField.tsx index b5a22fa..aad9756 100644 --- a/v2/frontend/src/components/SearchInputField.tsx +++ b/v2/frontend/src/components/SearchInputField.tsx @@ -41,6 +41,7 @@ const SearchInputField = ({ const handleSearch = async (e: { target: { value: string } }) => { const value = e.target.value; setInputValue(value); + setSearchInputText(value); }; const setSearchName = (value: string) => { diff --git a/v2/frontend/src/pages/SendPage.tsx b/v2/frontend/src/pages/SendPage.tsx index 39b2e56..8b9401f 100644 --- a/v2/frontend/src/pages/SendPage.tsx +++ b/v2/frontend/src/pages/SendPage.tsx @@ -2,7 +2,6 @@ import styled from "styled-components"; import { useRef, useState } from "react"; import SearchInputField from "../components/SearchInputField"; import { Link } from "react-router"; -import axios from "axios"; import ImageUploader from "../components/ImageUploader"; import { sendMessage } from "../api/messages"; @@ -13,6 +12,9 @@ const SendPage = () => { const [file, setFile] = useState(null); const handleSubmit = async () => { + if (!searchInputText) return alert("받는이를 입력해주세요."); + if (!messageTextAreaRef.current?.value) + return alert("메시지 내용을 입력해주세요."); const formData = new FormData(); formData.append("receiverName", searchInputText); @@ -21,11 +23,13 @@ const SendPage = () => { formData.append("image", file); } + console.log(formData); try { - const response = await sendMessage({data: formData}); + const response = await sendMessage(formData ); alert("메시지가 성공적으로 전송되었습니다."); } catch (error) { alert(error); + console.log(error); } }; From 2c830878c572bc62e0b4ac38db906936c86f7de8 Mon Sep 17 00:00:00 2001 From: Jihyun Kim Date: Fri, 24 Jan 2025 15:59:09 +0900 Subject: [PATCH 071/104] =?UTF-8?q?[FE]=20FEAT:=20accessType=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=9D=BC=20routing=20=EC=84=A4=EC=A0=95=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/pages/LoginPage.tsx | 6 ++++-- v2/frontend/src/pages/RegisterPage.tsx | 5 +++++ v2/frontend/src/routes.tsx | 13 +++++++------ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/v2/frontend/src/pages/LoginPage.tsx b/v2/frontend/src/pages/LoginPage.tsx index 09f7bd2..eeb1984 100644 --- a/v2/frontend/src/pages/LoginPage.tsx +++ b/v2/frontend/src/pages/LoginPage.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; import { Link, useNavigate } from "react-router-dom"; import styled from "styled-components"; import UserInputField from "../components/UserInputField"; @@ -10,6 +10,8 @@ const LoginPage = () => { const [pw, setPw] = useState(""); const navigate = useNavigate(); + useEffect(() => {}); + const handleLogin = async () => { const idRegex = /^[A-Za-z0-9]{1,10}$/; const pwRegex = /^[A-Za-z0-9]+$/; @@ -27,7 +29,7 @@ const LoginPage = () => { const data = { name: id, password: pw }; const response = await login(data); if (response.status === 200) { - navigate("/list"); + navigate("/"); } } catch (error: any) { alert(error.response.data.message); diff --git a/v2/frontend/src/pages/RegisterPage.tsx b/v2/frontend/src/pages/RegisterPage.tsx index 5acb812..62ed2cb 100644 --- a/v2/frontend/src/pages/RegisterPage.tsx +++ b/v2/frontend/src/pages/RegisterPage.tsx @@ -3,10 +3,12 @@ import { ReactComponent as NewYearImg } from "../assets/images/newYear.svg"; import styled from "styled-components"; import UserInputField from "../components/UserInputField"; import { register } from "../api/users"; +import { useNavigate } from "react-router"; const RegisterPage = () => { const [id, setId] = useState(""); const [pw, setPw] = useState(""); + const navigate = useNavigate(); const handleRegister = async () => { const idRegex = /^[A-Za-z0-9]{1,10}$/; @@ -24,6 +26,9 @@ const RegisterPage = () => { try { const data = { name: id, password: pw }; const response = await register(data); + if (response.status === 200) { + navigate("/login"); + } } catch (error: any) { alert(error.response.data.message); } diff --git a/v2/frontend/src/routes.tsx b/v2/frontend/src/routes.tsx index e0929e1..74f1e18 100644 --- a/v2/frontend/src/routes.tsx +++ b/v2/frontend/src/routes.tsx @@ -4,6 +4,7 @@ import RegisterPage from "./pages/RegisterPage"; import ListPage from "./pages/ListPage"; import { ReactElement } from "react"; import PrivateRoute from "./components/PrivateRoute"; +import PublicRoute from "./components/PublicRoute"; enum AccessType { PUBLIC, @@ -19,6 +20,11 @@ export interface RouteInfo { const routesInfo: RouteInfo[] = [ { path: "/", + accessType: AccessType.PRIVATE, + element: , + }, + { + path: "/login", accessType: AccessType.PUBLIC, element: , }, @@ -27,14 +33,9 @@ const routesInfo: RouteInfo[] = [ accessType: AccessType.PUBLIC, element: , }, - { - path: "/list", - accessType: AccessType.PUBLIC, // PRIVATE - element: , - }, { path: "/send", - accessType: AccessType.PUBLIC, // PRIVATE + accessType: AccessType.PRIVATE, element: , }, ]; From d6db73affa6af8391ac4dd6cf9ea034b7d938ff2 Mon Sep 17 00:00:00 2001 From: Jihyun Kim Date: Fri, 24 Jan 2025 15:59:38 +0900 Subject: [PATCH 072/104] =?UTF-8?q?[FE]=20FEAT:=20public=20route=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/components/PublicRoute.tsx | 41 ++++++++++++++++++++++ v2/frontend/src/routes.tsx | 2 ++ 2 files changed, 43 insertions(+) create mode 100644 v2/frontend/src/components/PublicRoute.tsx diff --git a/v2/frontend/src/components/PublicRoute.tsx b/v2/frontend/src/components/PublicRoute.tsx new file mode 100644 index 0000000..cb54292 --- /dev/null +++ b/v2/frontend/src/components/PublicRoute.tsx @@ -0,0 +1,41 @@ +import { ReactElement, useEffect, useState } from "react"; +import { checkAuth } from "../api/users"; +import { Navigate, useLocation } from "react-router"; + +const PublicRoute = ({ + children, +}: { + children: ReactElement; +}): ReactElement => { + const [isLoading, setIsLoading] = useState(true); + const [isAuthenticated, setIsAuthenticated] = useState(false); + const location = useLocation(); + + useEffect(() => { + // auth check - call "/user/auth" API with token + const checkAuthStatus = async () => { + try { + const res = await checkAuth(); + setIsAuthenticated(res.status === 200); + } catch (error) { + setIsAuthenticated(false); + } finally { + setIsLoading(false); + } + }; + + checkAuthStatus(); + }, []); + + if (isLoading) { + return
Loading...
; + } + + if (isAuthenticated) { + return ; + } + + return children; +}; + +export default PublicRoute; diff --git a/v2/frontend/src/routes.tsx b/v2/frontend/src/routes.tsx index 74f1e18..ddc8309 100644 --- a/v2/frontend/src/routes.tsx +++ b/v2/frontend/src/routes.tsx @@ -44,6 +44,8 @@ const injectProtectedRoute = (routesInfo: RouteInfo[]) => { return routesInfo.map((route: RouteInfo) => { if (route.accessType === AccessType.PRIVATE) { route.element = {route.element}; + } else if (route.accessType === AccessType.PUBLIC) { + route.element = {route.element}; } return route; From c9a19c497144890734f70810b971fcff4cab959d Mon Sep 17 00:00:00 2001 From: Jihyun Kim Date: Fri, 24 Jan 2025 16:04:44 +0900 Subject: [PATCH 073/104] =?UTF-8?q?[FE]=20FIX:=20list=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EB=A6=AC=EB=8B=A4=EC=9D=B4=EB=A0=89=EC=85=98=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/pages/SendPage.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/v2/frontend/src/pages/SendPage.tsx b/v2/frontend/src/pages/SendPage.tsx index 8b9401f..f0f9bc1 100644 --- a/v2/frontend/src/pages/SendPage.tsx +++ b/v2/frontend/src/pages/SendPage.tsx @@ -23,9 +23,9 @@ const SendPage = () => { formData.append("image", file); } - console.log(formData); + console.log(formData); try { - const response = await sendMessage(formData ); + const response = await sendMessage(formData); alert("메시지가 성공적으로 전송되었습니다."); } catch (error) { alert(error); @@ -36,7 +36,7 @@ const SendPage = () => { return ( - 덕담 보러 가기 + 덕담 보러 가기 덕담 보내기 From 5963e0b06695e0094f48280e50e60fd9008d4d75 Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Fri, 24 Jan 2025 16:07:12 +0900 Subject: [PATCH 074/104] =?UTF-8?q?[FE]=20FEAT:=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8/=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20=EB=AC=B8?= =?UTF-8?q?=EA=B5=AC=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20pattern=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=8D=BC=ED=8B=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/components/UserInputField.tsx | 7 +++++-- v2/frontend/src/pages/LoginPage.tsx | 18 +++++------------- v2/frontend/src/pages/RegisterPage.tsx | 18 +++++------------- 3 files changed, 15 insertions(+), 28 deletions(-) diff --git a/v2/frontend/src/components/UserInputField.tsx b/v2/frontend/src/components/UserInputField.tsx index 5c3221b..8315ed9 100644 --- a/v2/frontend/src/components/UserInputField.tsx +++ b/v2/frontend/src/components/UserInputField.tsx @@ -3,16 +3,18 @@ import styled from "styled-components"; type LoginInputFieldProps = { type?: string; - autocomplete?: string; value: string; + pattern?: string; + autocomplete?: string; onChange: (e: React.ChangeEvent) => void; placeholder?: string; }; const LoginInputField: React.FC = ({ type, - autocomplete, value, + pattern, + autocomplete, onChange, placeholder, }) => { @@ -23,6 +25,7 @@ const LoginInputField: React.FC = ({ { const [id, setId] = useState(""); const [pw, setPw] = useState(""); const navigate = useNavigate(); + const idRegex = "/^[A-Za-z0-9]{1,10}$/"; + const pwRegex = "/^(?!.*(.)\\1{3})[A-Za-z0-9]+$/"; useEffect(() => {}); const handleLogin = async () => { - const idRegex = /^[A-Za-z0-9]{1,10}$/; - const pwRegex = /^[A-Za-z0-9]+$/; - const fourInARowRegex = /(.)\1{3}/; - - const isValid = - idRegex.test(id) && pwRegex.test(pw) && !fourInARowRegex.test(pw); - - if (!isValid) { - alert("로그인 실패"); - return; - } - try { const data = { name: id, password: pw }; const response = await login(data); @@ -39,11 +29,12 @@ const LoginPage = () => { return ( - CABI-Onboarding + 로그인
setId(e.target.value)} placeholder="id" @@ -51,6 +42,7 @@ const LoginPage = () => { setPw(e.target.value)} placeholder="pw" diff --git a/v2/frontend/src/pages/RegisterPage.tsx b/v2/frontend/src/pages/RegisterPage.tsx index 62ed2cb..730a615 100644 --- a/v2/frontend/src/pages/RegisterPage.tsx +++ b/v2/frontend/src/pages/RegisterPage.tsx @@ -9,20 +9,10 @@ const RegisterPage = () => { const [id, setId] = useState(""); const [pw, setPw] = useState(""); const navigate = useNavigate(); + const idRegex = "/^[A-Za-z0-9]{1,10}$/"; + const pwRegex = "/^(?!.*(.)\\1{3})[A-Za-z0-9]+$/"; const handleRegister = async () => { - const idRegex = /^[A-Za-z0-9]{1,10}$/; - const pwRegex = /^[A-Za-z0-9]+$/; - const fourInARowRegex = /(.)\1{3}/; - - const isValid = - idRegex.test(id) && pwRegex.test(pw) && !fourInARowRegex.test(pw); - - if (!isValid) { - alert("로그인 실패"); - return; - } - try { const data = { name: id, password: pw }; const response = await register(data); @@ -36,11 +26,12 @@ const RegisterPage = () => { return ( - CABI-Onboarding + 회원가입 setId(e.target.value)} placeholder="id" @@ -48,6 +39,7 @@ const RegisterPage = () => { setPw(e.target.value)} placeholder="pw" From 9aa716384504dbda83bfe0aff082367ac5f9d77c Mon Sep 17 00:00:00 2001 From: jiminChoi Date: Fri, 24 Jan 2025 16:19:44 +0900 Subject: [PATCH 075/104] =?UTF-8?q?[FE]=20FIX:=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=95=84=EC=9B=83=20=EA=B8=B0=EB=8A=A5=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/api/users.ts | 7 ++++++ v2/frontend/src/pages/SendPage.tsx | 38 +++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/v2/frontend/src/api/users.ts b/v2/frontend/src/api/users.ts index 6187b39..1fe863e 100644 --- a/v2/frontend/src/api/users.ts +++ b/v2/frontend/src/api/users.ts @@ -19,3 +19,10 @@ export const searchGroup = async (params: object) => { export const searchName = async (params: object) => { return service.get(`/users/search/name`, { params }); }; + + +// 벡엔드에서 구현 필요 +//localStorage.clear(); +export const logout = async () => { + return service.get("/users/logout"); +} \ No newline at end of file diff --git a/v2/frontend/src/pages/SendPage.tsx b/v2/frontend/src/pages/SendPage.tsx index f0f9bc1..e4d6622 100644 --- a/v2/frontend/src/pages/SendPage.tsx +++ b/v2/frontend/src/pages/SendPage.tsx @@ -1,15 +1,18 @@ import styled from "styled-components"; import { useRef, useState } from "react"; import SearchInputField from "../components/SearchInputField"; -import { Link } from "react-router"; +import { Link, useNavigate } from "react-router"; import ImageUploader from "../components/ImageUploader"; import { sendMessage } from "../api/messages"; +import { al } from "react-router/dist/development/fog-of-war-DLtn2OLr"; +import { logout } from "../api/users"; const SendPage = () => { const [searchInputText, setSearchInputText] = useState(""); const messageTextAreaRef = useRef(null); const [isFocused, setIsFocused] = useState(false); const [file, setFile] = useState(null); + const navigate = useNavigate(); const handleSubmit = async () => { if (!searchInputText) return alert("받는이를 입력해주세요."); @@ -33,8 +36,22 @@ const SendPage = () => { } }; + + const HandleLogout = async() => { + try { + const response = await logout(); + alert("로그아웃."); + navigate("/login"); + } catch (error) { + alert(error); + } +} + return ( + + + 덕담 보러 가기 @@ -87,6 +104,25 @@ const WrapperStyled = styled.div` padding: 60px 0; `; +const LogoutWrapperStyled = styled.div` +width: 100%; +display: flex; +justify-content: flex-start; +/* background-color: #f5f5f5; */ + +button { + width: 100px; + height: 30px; + /* background-color: #9747ff; */ + color: #999999; + /* font-weight: 700; */ + font-size: 0.875rem; + border: 1px solid #ffffff; + border-radius: 4px; + cursor: pointer; +} +`; + const LinkWrapperStyled = styled.div` width: 80%; height: 50px; From e449780bcf630c035b7fd9079b0b362534c6d167 Mon Sep 17 00:00:00 2001 From: JunSeong Date: Fri, 24 Jan 2025 16:25:33 +0900 Subject: [PATCH 076/104] =?UTF-8?q?[FE]=20FEAT:=20list=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/components/ListPage/ListInterfaces.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 v2/frontend/src/components/ListPage/ListInterfaces.tsx diff --git a/v2/frontend/src/components/ListPage/ListInterfaces.tsx b/v2/frontend/src/components/ListPage/ListInterfaces.tsx new file mode 100644 index 0000000..f59557b --- /dev/null +++ b/v2/frontend/src/components/ListPage/ListInterfaces.tsx @@ -0,0 +1,9 @@ +interface Message { + messageId: string; + senderName: string; + receiverName: string; + context: string; + imageUrl: string; +} + +export default Message; From 0a2524cdd4096fa307768fefccf2e31bf52bbc32 Mon Sep 17 00:00:00 2001 From: JunSeong Date: Fri, 24 Jan 2025 16:25:50 +0900 Subject: [PATCH 077/104] =?UTF-8?q?[FE]=20FEAT:=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EB=B2=84=ED=8A=BC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/ListPage/CategoryButtons.tsx | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 v2/frontend/src/components/ListPage/CategoryButtons.tsx diff --git a/v2/frontend/src/components/ListPage/CategoryButtons.tsx b/v2/frontend/src/components/ListPage/CategoryButtons.tsx new file mode 100644 index 0000000..892f17b --- /dev/null +++ b/v2/frontend/src/components/ListPage/CategoryButtons.tsx @@ -0,0 +1,39 @@ +import styled from "styled-components"; +import { Filter } from "../../constant"; + +const CategoryButtons = ({ + currentCategory, + handleChangedCategory, +}: { + currentCategory: Filter; + handleChangedCategory: (category: Filter) => void; +}) => { + return ( +
+ handleChangedCategory(Filter.TO_EVERYONE)} + $isActived={currentCategory === Filter.TO_EVERYONE} + > + To.everyone + + handleChangedCategory(Filter.TO_ME)} + $isActived={currentCategory === Filter.TO_ME} + > + To.me + + handleChangedCategory(Filter.FROM_ME)} + $isActived={currentCategory === Filter.FROM_ME} + > + From.me + +
+ ); +}; + +const ButtonStyled = styled.button<{ $isActived: boolean }>` + background-color: ${(props) => (props.$isActived ? "red" : "blue")}; +`; + +export default CategoryButtons; From b45c0cc44feffc912d11016fb815fe024ece1055 Mon Sep 17 00:00:00 2001 From: JunSeong Date: Fri, 24 Jan 2025 16:26:16 +0900 Subject: [PATCH 078/104] =?UTF-8?q?[FE]=20FEAT:=20=EB=84=A4=EB=B9=84?= =?UTF-8?q?=EA=B2=8C=EC=9D=B4=EC=85=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/components/ListPage/ListTopNav.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 v2/frontend/src/components/ListPage/ListTopNav.tsx diff --git a/v2/frontend/src/components/ListPage/ListTopNav.tsx b/v2/frontend/src/components/ListPage/ListTopNav.tsx new file mode 100644 index 0000000..bcbc5c3 --- /dev/null +++ b/v2/frontend/src/components/ListPage/ListTopNav.tsx @@ -0,0 +1,11 @@ +import { Link } from "react-router"; + +const ListTopNav = () => { + return ( + + ); +}; + +export default ListTopNav; From b96a7e0a1d3370de87eb21c3b792b90de5786eb7 Mon Sep 17 00:00:00 2001 From: JunSeong Date: Fri, 24 Jan 2025 16:26:41 +0900 Subject: [PATCH 079/104] =?UTF-8?q?[FE]=20FEAT:=20=EB=8D=95=EB=8B=B4=20?= =?UTF-8?q?=EB=B0=95=EC=8A=A4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/ListPage/MessageBox.tsx | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 v2/frontend/src/components/ListPage/MessageBox.tsx diff --git a/v2/frontend/src/components/ListPage/MessageBox.tsx b/v2/frontend/src/components/ListPage/MessageBox.tsx new file mode 100644 index 0000000..1aa354d --- /dev/null +++ b/v2/frontend/src/components/ListPage/MessageBox.tsx @@ -0,0 +1,32 @@ +import { styled } from "styled-components"; + +interface MessageBoxProps { + senderName: string; + receiverName: string; + context: string; + imageUrl: string; +} + +const MessageBox = ({ + senderName, + receiverName, + context, + imageUrl, +}: MessageBoxProps) => { + return ( + <> + +
+ from {senderName} to {receiverName} +
+
{context}
+ + ); +}; + +const ImgStyled = styled.img` + width: 100px; + height: 100px; +`; + +export default MessageBox; From 85e6362d92d9d2bb370b621be6bf383a1f362cbd Mon Sep 17 00:00:00 2001 From: JunSeong Date: Fri, 24 Jan 2025 16:26:55 +0900 Subject: [PATCH 080/104] =?UTF-8?q?[FE]=20FEAT:=20=EB=8D=95=EB=8B=B4=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/ListPage/MessageList.tsx | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 v2/frontend/src/components/ListPage/MessageList.tsx diff --git a/v2/frontend/src/components/ListPage/MessageList.tsx b/v2/frontend/src/components/ListPage/MessageList.tsx new file mode 100644 index 0000000..956a40f --- /dev/null +++ b/v2/frontend/src/components/ListPage/MessageList.tsx @@ -0,0 +1,36 @@ +import styled from "styled-components"; +import MessageBox from "./MessageBox"; +import Message from "./ListInterfaces"; + +interface MessageListProps { + items: Message[]; + isLoading: boolean; +} + +const MessageList = ({ items, isLoading }: MessageListProps) => { + if (items.length === 0 && !isLoading) { + return 메시지가 없습니다; + } + + return ( +
+ {items.map((item) => ( + + ))} +
+ ); +}; + +const EmptyState = styled.div` + text-align: center; + padding: 2rem; + color: #666; +`; + +export default MessageList; From d524537a6dba9928d8f12378477affbd4ba9520d Mon Sep 17 00:00:00 2001 From: JunSeong Date: Fri, 24 Jan 2025 16:27:13 +0900 Subject: [PATCH 081/104] =?UTF-8?q?[FE]=20FEAT:=20=EB=8D=95=EB=8B=B4=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/pages/ListPage.tsx | 99 ++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 33 deletions(-) diff --git a/v2/frontend/src/pages/ListPage.tsx b/v2/frontend/src/pages/ListPage.tsx index 4dd7d97..2d54f49 100644 --- a/v2/frontend/src/pages/ListPage.tsx +++ b/v2/frontend/src/pages/ListPage.tsx @@ -1,53 +1,86 @@ -import { useEffect, useState } from "react"; -import { Link } from "react-router-dom"; +import { useCallback, useEffect, useState } from "react"; import { Filter, LIST_SIZE } from "../constant"; import { getMessages } from "../api/messages"; +import styled from "styled-components"; +import MessageList from "../components/ListPage/MessageList"; +import Message from "../components/ListPage/ListInterfaces"; +import CategoryButtons from "../components/ListPage/CategoryButtons"; +import ListTopNav from "../components/ListPage/ListTopNav"; const ListPage = () => { - const [items, setItems] = useState([]); - const [page, setPage] = useState(0); + const [items, setItems] = useState([]); + const [nextPage, setNextPage] = useState(0); const [isLoading, setIsLoading] = useState(false); const [category, setCategory] = useState(Filter.TO_EVERYONE); + const [isLast, setIsLast] = useState(false); + + const fetchItems = useCallback(async () => { + if (isLast || isLoading) return; - const fetchItems = async () => { try { setIsLoading(true); - const res = await getMessages({ page, size: LIST_SIZE, category }); - setItems((items) => [...items, ...res.data.messages]); - setPage((page) => page + 1); + const res = await getMessages({ + page: nextPage, + size: LIST_SIZE, + category, + }); + + if (res.data.messages.length === 0) { + setIsLast(true); + return; + } + + setItems((prev) => + nextPage === 0 ? res.data.messages : [...prev, ...res.data.messages] + ); + setNextPage(res.data.currentPage + 1); + setIsLast(res.data.currentPage >= res.data.totalLength); + } catch (error) { + console.error("Failed to fetch messages:", error); + } finally { setIsLoading(false); - } catch (error) {} + } + }, [category, nextPage]); + + const handleChangedCategory = (category: Filter) => { + setCategory(category); + setNextPage(0); + setItems([]); + setIsLast(false); }; useEffect(() => { fetchItems(); - }, []); + }, [category]); return ( - <> - 덕담 보내러 가기 -

덕담 보기

-
- - - -
-
-
리스트
- {items.map((item: any, index) => ( -
-
{item.senderName}
-
{item.receiverName}
-
{item.context}
-
{item.imageUrl}
-
- ))} -
-
{isLoading ? `Loading...` : `더보기`}
- +
+ + +
+

덕담 보기

+
+ +
+ {/*
+
카테고리: {category}
+
페이지: {nextPage}
+
*/} + + + + {isLoading ? `Loading...` : `더보기`}{" "} + +
+
); }; +const MoreButtonStyled = styled.button<{ $isLast: boolean }>` + display: ${(props) => (props.$isLast ? "none" : "block")}; +`; + export default ListPage; From 22560c0f2918f15440dbe331c60be407d32910e3 Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Fri, 24 Jan 2025 17:00:02 +0900 Subject: [PATCH 082/104] =?UTF-8?q?[FE]=20FIX:=20=EC=A0=95=EA=B7=9C?= =?UTF-8?q?=ED=91=9C=ED=98=84=EC=8B=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/pages/LoginPage.tsx | 8 ++++---- v2/frontend/src/pages/RegisterPage.tsx | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/v2/frontend/src/pages/LoginPage.tsx b/v2/frontend/src/pages/LoginPage.tsx index 2e692c6..df82f1b 100644 --- a/v2/frontend/src/pages/LoginPage.tsx +++ b/v2/frontend/src/pages/LoginPage.tsx @@ -9,8 +9,8 @@ const LoginPage = () => { const [id, setId] = useState(""); const [pw, setPw] = useState(""); const navigate = useNavigate(); - const idRegex = "/^[A-Za-z0-9]{1,10}$/"; - const pwRegex = "/^(?!.*(.)\\1{3})[A-Za-z0-9]+$/"; + const idRegex = /^[A-Za-z0-9]{1,10}$/; + const pwRegex = /^(?!.*(.)\1{3})[A-Za-z0-9]+$/; useEffect(() => {}); @@ -34,7 +34,7 @@ const LoginPage = () => { setId(e.target.value)} placeholder="id" @@ -42,7 +42,7 @@ const LoginPage = () => { setPw(e.target.value)} placeholder="pw" diff --git a/v2/frontend/src/pages/RegisterPage.tsx b/v2/frontend/src/pages/RegisterPage.tsx index 730a615..06699ca 100644 --- a/v2/frontend/src/pages/RegisterPage.tsx +++ b/v2/frontend/src/pages/RegisterPage.tsx @@ -9,8 +9,8 @@ const RegisterPage = () => { const [id, setId] = useState(""); const [pw, setPw] = useState(""); const navigate = useNavigate(); - const idRegex = "/^[A-Za-z0-9]{1,10}$/"; - const pwRegex = "/^(?!.*(.)\\1{3})[A-Za-z0-9]+$/"; + const idRegex = /^[A-Za-z0-9]{1,10}$/; + const pwRegex = /^(?!.*(.)\1{3})[A-Za-z0-9]+$/; const handleRegister = async () => { try { @@ -31,7 +31,7 @@ const RegisterPage = () => { setId(e.target.value)} placeholder="id" @@ -39,7 +39,7 @@ const RegisterPage = () => { setPw(e.target.value)} placeholder="pw" From 3f860abf57cf797231fb175c56e308fcf284419f Mon Sep 17 00:00:00 2001 From: Jihyun Kim Date: Fri, 24 Jan 2025 17:01:52 +0900 Subject: [PATCH 083/104] =?UTF-8?q?[FE]=20FIX:=20conflict=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/pages/SendPage.tsx | 55 +++++++++++++++++------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/v2/frontend/src/pages/SendPage.tsx b/v2/frontend/src/pages/SendPage.tsx index e4d6622..0230c50 100644 --- a/v2/frontend/src/pages/SendPage.tsx +++ b/v2/frontend/src/pages/SendPage.tsx @@ -13,6 +13,13 @@ const SendPage = () => { const [isFocused, setIsFocused] = useState(false); const [file, setFile] = useState(null); const navigate = useNavigate(); + const maxLength = 42; + + const handleInput = () => { + if (!messageTextAreaRef.current) return; + messageTextAreaRef.current.style.height = "auto"; + messageTextAreaRef.current.style.height = `${messageTextAreaRef.current.scrollHeight}px`; + }; const handleSubmit = async () => { if (!searchInputText) return alert("받는이를 입력해주세요."); @@ -36,8 +43,7 @@ const SendPage = () => { } }; - - const HandleLogout = async() => { + const HandleLogout = async () => { try { const response = await logout(); alert("로그아웃."); @@ -45,12 +51,12 @@ const SendPage = () => { } catch (error) { alert(error); } -} + }; return ( - + 덕담 보러 가기 @@ -70,6 +76,7 @@ const SendPage = () => { setIsFocused(true)} @@ -105,22 +112,22 @@ const WrapperStyled = styled.div` `; const LogoutWrapperStyled = styled.div` -width: 100%; -display: flex; -justify-content: flex-start; -/* background-color: #f5f5f5; */ - -button { - width: 100px; - height: 30px; - /* background-color: #9747ff; */ - color: #999999; - /* font-weight: 700; */ - font-size: 0.875rem; - border: 1px solid #ffffff; - border-radius: 4px; - cursor: pointer; -} + width: 100%; + display: flex; + justify-content: flex-start; + /* background-color: #f5f5f5; */ + + button { + width: 100px; + height: 30px; + /* background-color: #9747ff; */ + color: #999999; + /* font-weight: 700; */ + font-size: 0.875rem; + border: 1px solid #ffffff; + border-radius: 4px; + cursor: pointer; + } `; const LinkWrapperStyled = styled.div` @@ -176,10 +183,12 @@ const FormSubTitleStyled = styled.h3` `; const SendTextFieldStyled = styled.textarea<{ $isFocus: boolean }>` - /* TODO: font 및 텍스트 크기 조정(통일) */ - /* font-family: "Noto Sans KR", sans-serif; */ + font-family: "Noto Sans KR", sans-serif; + font-size: 16px; width: 100%; - height: 40px; + /* min-height: 40px; */ + max-height: 200px; + resize: none; background-color: var(--ref-white); border-radius: 8px; border: 2px solid From 410d6a02885246922fb6ed8f59a78168c7f7be4c Mon Sep 17 00:00:00 2001 From: Jihyun Kim Date: Fri, 24 Jan 2025 17:39:35 +0900 Subject: [PATCH 084/104] =?UTF-8?q?[FE]=20FEAT:=20=EA=B8=80=EC=9E=90?= =?UTF-8?q?=EC=88=98=20=EC=A0=9C=ED=95=9C=20=EB=B0=8F=20=EC=84=B8=EB=8A=94?= =?UTF-8?q?=20=EB=B6=80=EB=B6=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/pages/SendPage.tsx | 47 ++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/v2/frontend/src/pages/SendPage.tsx b/v2/frontend/src/pages/SendPage.tsx index 0230c50..7c8cff5 100644 --- a/v2/frontend/src/pages/SendPage.tsx +++ b/v2/frontend/src/pages/SendPage.tsx @@ -1,5 +1,5 @@ import styled from "styled-components"; -import { useRef, useState } from "react"; +import { ChangeEvent, useRef, useState } from "react"; import SearchInputField from "../components/SearchInputField"; import { Link, useNavigate } from "react-router"; import ImageUploader from "../components/ImageUploader"; @@ -8,17 +8,26 @@ import { al } from "react-router/dist/development/fog-of-war-DLtn2OLr"; import { logout } from "../api/users"; const SendPage = () => { - const [searchInputText, setSearchInputText] = useState(""); + const [searchInputText, setSearchInputText] = useState(""); const messageTextAreaRef = useRef(null); - const [isFocused, setIsFocused] = useState(false); + const [text, setText] = useState(""); + const [textLength, setTextLength] = useState(0); + const [isFocused, setIsFocused] = useState(false); const [file, setFile] = useState(null); const navigate = useNavigate(); const maxLength = 42; - const handleInput = () => { - if (!messageTextAreaRef.current) return; - messageTextAreaRef.current.style.height = "auto"; - messageTextAreaRef.current.style.height = `${messageTextAreaRef.current.scrollHeight}px`; + const handleInputChanged = (e: ChangeEvent) => { + // 글자 폭에 따른 자동 길이 조절 + const currentValue = messageTextAreaRef.current; + if (!currentValue) return; + currentValue.style.height = "auto"; + currentValue.style.height = `${currentValue.scrollHeight}px`; + + // 글자 수 세기 및 제한 + const inputText = e.target.value.slice(0, maxLength); + setText(inputText); + setTextLength(inputText.length); }; const handleSubmit = async () => { @@ -35,7 +44,7 @@ const SendPage = () => { console.log(formData); try { - const response = await sendMessage(formData); + await sendMessage(formData); alert("메시지가 성공적으로 전송되었습니다."); } catch (error) { alert(error); @@ -45,7 +54,7 @@ const SendPage = () => { const HandleLogout = async () => { try { - const response = await logout(); + await logout(); alert("로그아웃."); navigate("/login"); } catch (error) { @@ -76,13 +85,16 @@ const SendPage = () => { setIsFocused(true)} onBlur={() => setIsFocused(false)} /> - {/* textarea length */} + + {textLength} / {maxLength} + @@ -115,14 +127,11 @@ const LogoutWrapperStyled = styled.div` width: 100%; display: flex; justify-content: flex-start; - /* background-color: #f5f5f5; */ button { width: 100px; height: 30px; - /* background-color: #9747ff; */ color: #999999; - /* font-weight: 700; */ font-size: 0.875rem; border: 1px solid #ffffff; border-radius: 4px; @@ -186,7 +195,7 @@ const SendTextFieldStyled = styled.textarea<{ $isFocus: boolean }>` font-family: "Noto Sans KR", sans-serif; font-size: 16px; width: 100%; - /* min-height: 40px; */ + min-height: 40px; max-height: 200px; resize: none; background-color: var(--ref-white); @@ -200,6 +209,14 @@ const SendTextFieldStyled = styled.textarea<{ $isFocus: boolean }>` } `; +const TextLengthStyled = styled.div` + font-family: "Noto Sans KR", sans-serif; + font-size: 12px; + color: var(--ref-gray-600); + text-align: right; + margin-top: 4; +`; + const FormButtonContainerStyled = styled.div` width: 100%; display: flex; From 922eddf75e8716ca1cf870e434e76069d53cae25 Mon Sep 17 00:00:00 2001 From: JunSeong Date: Fri, 24 Jan 2025 17:53:20 +0900 Subject: [PATCH 085/104] =?UTF-8?q?[FE]=20FEAT:=20=EC=9D=B4=EB=AF=B8?= =?UTF-8?q?=EC=A7=80=20=EB=AA=A8=EB=8B=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ListPage/section/MessageList.tsx | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 v2/frontend/src/components/ListPage/section/MessageList.tsx diff --git a/v2/frontend/src/components/ListPage/section/MessageList.tsx b/v2/frontend/src/components/ListPage/section/MessageList.tsx new file mode 100644 index 0000000..b07cf23 --- /dev/null +++ b/v2/frontend/src/components/ListPage/section/MessageList.tsx @@ -0,0 +1,78 @@ +import { useState } from "react"; +import styled from "styled-components"; +import Message from "../ListInterfaces"; +import MessageBox from "./MessageBox"; + +interface MessageListProps { + items: Message[]; + isLoading: boolean; +} + +const MessageList = ({ items, isLoading }: MessageListProps) => { + const [showModal, setShowModal] = useState(false); + const [modalImgUrl, setModalImgUrl] = useState(""); + + if (items.length === 0 && !isLoading) { + return 메시지가 없습니다; + } + + return ( + <> + + {items.map((item) => ( + + ))} + + + {showModal && ( + setShowModal(false)}> + e.stopPropagation()} + /> + + )} + + ); +}; + +const MessageBoxStyled = styled.div` + margin-top: 32px; +`; + +const EmptyState = styled.div` + text-align: center; + padding: 2rem; + color: #666; +`; + +const ModalOverlay = styled.div` + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.7); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +`; + +const ModalImage = styled.img` + max-width: fit-content; + max-height: 90%; + border-radius: 8px; + object-fit: contain; +`; + +export default MessageList; From b0dfc6769b3d44bb1484861efdb6ac8e8b1e98af Mon Sep 17 00:00:00 2001 From: JunSeong Date: Fri, 24 Jan 2025 17:54:09 +0900 Subject: [PATCH 086/104] =?UTF-8?q?[FE]=20FEAT:=20list=20page=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/ListPage/ListHeader.tsx | 23 +++++ .../src/components/ListPage/ListSection.tsx | 71 ++++++++++++++ .../src/components/ListPage/ListTopNav.tsx | 17 +++- .../src/components/ListPage/MessageBox.tsx | 32 ------ .../src/components/ListPage/MessageList.tsx | 36 ------- .../{ => section}/CategoryButtons.tsx | 23 ++++- .../ListPage/section/MessageBox.tsx | 72 ++++++++++++++ .../ListPage/section/MoreButtons.tsx | 21 ++++ v2/frontend/src/pages/ListPage.tsx | 97 ++++++------------- 9 files changed, 247 insertions(+), 145 deletions(-) create mode 100644 v2/frontend/src/components/ListPage/ListHeader.tsx create mode 100644 v2/frontend/src/components/ListPage/ListSection.tsx delete mode 100644 v2/frontend/src/components/ListPage/MessageBox.tsx delete mode 100644 v2/frontend/src/components/ListPage/MessageList.tsx rename v2/frontend/src/components/ListPage/{ => section}/CategoryButtons.tsx (63%) create mode 100644 v2/frontend/src/components/ListPage/section/MessageBox.tsx create mode 100644 v2/frontend/src/components/ListPage/section/MoreButtons.tsx diff --git a/v2/frontend/src/components/ListPage/ListHeader.tsx b/v2/frontend/src/components/ListPage/ListHeader.tsx new file mode 100644 index 0000000..64655c4 --- /dev/null +++ b/v2/frontend/src/components/ListPage/ListHeader.tsx @@ -0,0 +1,23 @@ +import styled from "styled-components"; + +const ListHeader = () => { + return ( + <> + 덕담 보기 + + ); +}; + +export default ListHeader; + +const TitleContainerStyled = styled.header` + display: flex; + justify-content: center; + align-items: center; + border-bottom: 2px solid var(--service-man-title-border-btm-color); + margin-bottom: 70px; + font-weight: 700; + font-size: 1.25rem; + letter-spacing: -0.02rem; + margin-bottom: 20px; +`; diff --git a/v2/frontend/src/components/ListPage/ListSection.tsx b/v2/frontend/src/components/ListPage/ListSection.tsx new file mode 100644 index 0000000..803a7ce --- /dev/null +++ b/v2/frontend/src/components/ListPage/ListSection.tsx @@ -0,0 +1,71 @@ +import { useCallback, useEffect, useState } from "react"; +import { Filter, LIST_SIZE } from "../../constant"; +import Message from "./ListInterfaces"; +import CategoryButtons from "./section/CategoryButtons"; +import MessageList from "./section/MessageList"; +import MoreButtons from "./section/MoreButtons"; +import { getMessages } from "../../api/messages"; + +const ListSection = () => { + const [items, setItems] = useState([]); + const [nextPage, setNextPage] = useState(0); + const [isLoading, setIsLoading] = useState(false); + const [category, setCategory] = useState(Filter.TO_EVERYONE); + const [isLast, setIsLast] = useState(false); + + const fetchItems = useCallback(async () => { + if (isLast || isLoading) return; + + try { + setIsLoading(true); + const res = await getMessages({ + page: nextPage, + size: LIST_SIZE, + category, + }); + + if (res.data.messages.length === 0) { + setIsLast(true); + return; + } + + setItems((prev) => + nextPage === 0 ? res.data.messages : [...prev, ...res.data.messages] + ); + setNextPage(res.data.currentPage + 1); + setIsLast(res.data.currentPage >= res.data.totalLength); + } catch (error) { + console.error("Failed to fetch messages:", error); + } finally { + setIsLoading(false); + } + }, [category, nextPage]); + + const handleChangedCategory = (category: Filter) => { + setCategory(category); + setNextPage(0); + setItems([]); + setIsLast(false); + }; + + useEffect(() => { + fetchItems(); + }, [category]); + + return ( + <> + + + + + ); +}; + +export default ListSection; diff --git a/v2/frontend/src/components/ListPage/ListTopNav.tsx b/v2/frontend/src/components/ListPage/ListTopNav.tsx index bcbc5c3..74c36e1 100644 --- a/v2/frontend/src/components/ListPage/ListTopNav.tsx +++ b/v2/frontend/src/components/ListPage/ListTopNav.tsx @@ -1,11 +1,22 @@ import { Link } from "react-router"; +import styled from "styled-components"; const ListTopNav = () => { return ( - + <> + + 덕담 보내러 가기 + + ); }; +const LinkWrapperStyled = styled.div` + width: 100%; + display: flex; + justify-content: flex-end; + color: #9747ff; + font-size: 0.875rem; +`; + export default ListTopNav; diff --git a/v2/frontend/src/components/ListPage/MessageBox.tsx b/v2/frontend/src/components/ListPage/MessageBox.tsx deleted file mode 100644 index 1aa354d..0000000 --- a/v2/frontend/src/components/ListPage/MessageBox.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { styled } from "styled-components"; - -interface MessageBoxProps { - senderName: string; - receiverName: string; - context: string; - imageUrl: string; -} - -const MessageBox = ({ - senderName, - receiverName, - context, - imageUrl, -}: MessageBoxProps) => { - return ( - <> - -
- from {senderName} to {receiverName} -
-
{context}
- - ); -}; - -const ImgStyled = styled.img` - width: 100px; - height: 100px; -`; - -export default MessageBox; diff --git a/v2/frontend/src/components/ListPage/MessageList.tsx b/v2/frontend/src/components/ListPage/MessageList.tsx deleted file mode 100644 index 956a40f..0000000 --- a/v2/frontend/src/components/ListPage/MessageList.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import styled from "styled-components"; -import MessageBox from "./MessageBox"; -import Message from "./ListInterfaces"; - -interface MessageListProps { - items: Message[]; - isLoading: boolean; -} - -const MessageList = ({ items, isLoading }: MessageListProps) => { - if (items.length === 0 && !isLoading) { - return 메시지가 없습니다; - } - - return ( -
- {items.map((item) => ( - - ))} -
- ); -}; - -const EmptyState = styled.div` - text-align: center; - padding: 2rem; - color: #666; -`; - -export default MessageList; diff --git a/v2/frontend/src/components/ListPage/CategoryButtons.tsx b/v2/frontend/src/components/ListPage/section/CategoryButtons.tsx similarity index 63% rename from v2/frontend/src/components/ListPage/CategoryButtons.tsx rename to v2/frontend/src/components/ListPage/section/CategoryButtons.tsx index 892f17b..a87fe8a 100644 --- a/v2/frontend/src/components/ListPage/CategoryButtons.tsx +++ b/v2/frontend/src/components/ListPage/section/CategoryButtons.tsx @@ -1,5 +1,5 @@ import styled from "styled-components"; -import { Filter } from "../../constant"; +import { Filter } from "../../../constant"; const CategoryButtons = ({ currentCategory, @@ -9,7 +9,7 @@ const CategoryButtons = ({ handleChangedCategory: (category: Filter) => void; }) => { return ( -
+ handleChangedCategory(Filter.TO_EVERYONE)} $isActived={currentCategory === Filter.TO_EVERYONE} @@ -28,12 +28,27 @@ const CategoryButtons = ({ > From.me -
+ ); }; +const ButtonWrapper = styled.div` + background-color: #d0d0d0; + width: fit-content; + height: fit-content; + border-radius: 10px; + margin: 10px 0; +`; + const ButtonStyled = styled.button<{ $isActived: boolean }>` - background-color: ${(props) => (props.$isActived ? "red" : "blue")}; + background-color: ${(props) => (props.$isActived ? `#9747FF` : `#d0d0d0`)}; + width: fit-content; + height: 30px; + border-radius: 10px; + padding: 5px 10px; + color: ${(props) => (props.$isActived ? `#ffffff` : `#000000`)}; + font-size: 14px; + font-weight: 600; `; export default CategoryButtons; diff --git a/v2/frontend/src/components/ListPage/section/MessageBox.tsx b/v2/frontend/src/components/ListPage/section/MessageBox.tsx new file mode 100644 index 0000000..1dc36c3 --- /dev/null +++ b/v2/frontend/src/components/ListPage/section/MessageBox.tsx @@ -0,0 +1,72 @@ +import { styled } from "styled-components"; + +interface MessageBoxProps { + senderName: string; + receiverName: string; + context: string; + imageUrl: string; + setShowModal: (value: boolean) => void; + setModalImgUrl: (value: string) => void; +} + +const MessageBox = ({ + senderName, + receiverName, + context, + imageUrl, + setShowModal, + setModalImgUrl, +}: MessageBoxProps) => { + return ( + <> + + { + setShowModal(true); + setModalImgUrl(imageUrl); + }} + /> +
+ + from {senderName} to {receiverName} + + {context} +
+
+ + ); +}; + +const MessageBoxStyled = styled.div` + display: flex; + align-items: center; + padding: 1rem; + border: 1px solid #ddd; + border-radius: 28px; + margin-bottom: 1rem; +`; + +const ImgStyled = styled.img` + width: 100px; + height: 100px; + border-radius: 50%; + padding: 20px; +`; + +const SenderReceiverStyled = styled.div` + width: 100%; + color: #666666; + font-size: 14px; +`; + +const ContextStyled = styled.div` + width: 100%; + font-size: 20px; + font-weight: 700; + padding-top: 8px; + padding-bottom: 24px; +`; + +export default MessageBox; diff --git a/v2/frontend/src/components/ListPage/section/MoreButtons.tsx b/v2/frontend/src/components/ListPage/section/MoreButtons.tsx new file mode 100644 index 0000000..895a0c2 --- /dev/null +++ b/v2/frontend/src/components/ListPage/section/MoreButtons.tsx @@ -0,0 +1,21 @@ +import styled from "styled-components"; + +interface MoreButtonsProps { + fetchItems: () => void; + isLoading: boolean; + isLast: boolean; +} + +const MoreButtons = ({ fetchItems, isLoading, isLast }: MoreButtonsProps) => { + return ( + + {isLoading ? `Loading...` : `더보기`}{" "} + + ); +}; + +const MoreButtonStyled = styled.button<{ $isLast: boolean }>` + display: ${(props) => (props.$isLast ? "none" : "block")}; +`; + +export default MoreButtons; diff --git a/v2/frontend/src/pages/ListPage.tsx b/v2/frontend/src/pages/ListPage.tsx index 2d54f49..5860cca 100644 --- a/v2/frontend/src/pages/ListPage.tsx +++ b/v2/frontend/src/pages/ListPage.tsx @@ -1,86 +1,43 @@ -import { useCallback, useEffect, useState } from "react"; -import { Filter, LIST_SIZE } from "../constant"; -import { getMessages } from "../api/messages"; -import styled from "styled-components"; -import MessageList from "../components/ListPage/MessageList"; -import Message from "../components/ListPage/ListInterfaces"; -import CategoryButtons from "../components/ListPage/CategoryButtons"; import ListTopNav from "../components/ListPage/ListTopNav"; +import ListHeader from "../components/ListPage/ListHeader"; +import ListSection from "../components/ListPage/ListSection"; +import styled from "styled-components"; const ListPage = () => { - const [items, setItems] = useState([]); - const [nextPage, setNextPage] = useState(0); - const [isLoading, setIsLoading] = useState(false); - const [category, setCategory] = useState(Filter.TO_EVERYONE); - const [isLast, setIsLast] = useState(false); - - const fetchItems = useCallback(async () => { - if (isLast || isLoading) return; - - try { - setIsLoading(true); - const res = await getMessages({ - page: nextPage, - size: LIST_SIZE, - category, - }); - - if (res.data.messages.length === 0) { - setIsLast(true); - return; - } - - setItems((prev) => - nextPage === 0 ? res.data.messages : [...prev, ...res.data.messages] - ); - setNextPage(res.data.currentPage + 1); - setIsLast(res.data.currentPage >= res.data.totalLength); - } catch (error) { - console.error("Failed to fetch messages:", error); - } finally { - setIsLoading(false); - } - }, [category, nextPage]); - - const handleChangedCategory = (category: Filter) => { - setCategory(category); - setNextPage(0); - setItems([]); - setIsLast(false); - }; - - useEffect(() => { - fetchItems(); - }, [category]); - return ( -
- + + + +
-

덕담 보기

+
- {/*
-
카테고리: {category}
-
페이지: {nextPage}
-
*/} - - - - {isLoading ? `Loading...` : `더보기`}{" "} - +
-
+
); }; -const MoreButtonStyled = styled.button<{ $isLast: boolean }>` - display: ${(props) => (props.$isLast ? "none" : "block")}; +const WrapperStyled = styled.div` + font-family: "Noto Sans KR", sans-serif; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + padding: 60px 0; + overflow: scroll; + + & > * { + width: 80%; + max-width: 1000px; + } +`; + +const NavStyled = styled.nav` + width: 80%; `; export default ListPage; From 07a1730c23c1c5e3c8f02333eb92760526c95872 Mon Sep 17 00:00:00 2001 From: Jihyun Kim Date: Fri, 24 Jan 2025 17:56:25 +0900 Subject: [PATCH 087/104] =?UTF-8?q?[FE]=20FEAT:=20=EB=A6=AC=EB=8B=A4?= =?UTF-8?q?=EC=9D=B4=EB=A0=89=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/pages/SendPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/frontend/src/pages/SendPage.tsx b/v2/frontend/src/pages/SendPage.tsx index 7c8cff5..101b9bd 100644 --- a/v2/frontend/src/pages/SendPage.tsx +++ b/v2/frontend/src/pages/SendPage.tsx @@ -4,7 +4,6 @@ import SearchInputField from "../components/SearchInputField"; import { Link, useNavigate } from "react-router"; import ImageUploader from "../components/ImageUploader"; import { sendMessage } from "../api/messages"; -import { al } from "react-router/dist/development/fog-of-war-DLtn2OLr"; import { logout } from "../api/users"; const SendPage = () => { @@ -46,6 +45,7 @@ const SendPage = () => { try { await sendMessage(formData); alert("메시지가 성공적으로 전송되었습니다."); + navigate("/"); } catch (error) { alert(error); console.log(error); From 4c57be19da6a60b9a8ecd2600f53350a7171a7d1 Mon Sep 17 00:00:00 2001 From: lamodadite Date: Fri, 24 Jan 2025 20:44:37 +0900 Subject: [PATCH 088/104] =?UTF-8?q?FIX=20:=20=EC=BF=A0=ED=82=A4=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80,?= =?UTF-8?q?=20=EA=B7=B8=EB=A3=B9=EC=97=90=EA=B2=8C=20=EB=A9=94=EC=8B=9C?= =?UTF-8?q?=EC=A7=80=20=EB=B3=B4=EB=82=BC=EB=95=8C=20=EC=97=AC=EB=9F=AC?= =?UTF-8?q?=EA=B0=9C=20=EA=B0=80=EB=8A=94=20=ED=98=84=EC=83=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../message/controller/MessageController.java | 2 +- .../message/service/MessageService.java | 35 ++++--------------- .../user/service/UserService.java | 5 +++ 3 files changed, 12 insertions(+), 30 deletions(-) diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/controller/MessageController.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/controller/MessageController.java index a99ac6e..4c0599b 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/controller/MessageController.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/controller/MessageController.java @@ -37,7 +37,7 @@ public class MessageController { * @throws IOException */ @PostMapping("") - public void sendMessage(@CookieValue(name = "userName") String userName, + public void sendMessage(@CookieValue(name = "userName", required = false) String userName, @ModelAttribute MessageRequestDto messageData) throws IOException { messageService.sendMessage(userName, messageData); } diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java index fdfe170..2791698 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java @@ -1,7 +1,5 @@ package com.cabi.greetingCard.message.service; -import static com.cabi.greetingCard.user.domain.GroupNames.GROUP_EVERYONE; - import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.model.ObjectMetadata; import com.cabi.greetingCard.dto.MessageRequestDto; @@ -12,8 +10,8 @@ import com.cabi.greetingCard.message.domain.Message; import com.cabi.greetingCard.message.domain.MessageCategory; import com.cabi.greetingCard.message.repository.MessageRepository; -import com.cabi.greetingCard.user.domain.User; import com.cabi.greetingCard.user.repository.UserRepository; +import com.cabi.greetingCard.user.service.UserService; import java.io.IOException; import java.time.LocalDateTime; import java.util.List; @@ -41,6 +39,7 @@ public class MessageService { private final AmazonS3 s3Client; private final UserRepository userRepository; private final MessageMapper messageMapper; + private final UserService userService; @Value("${cloud.aws.s3.bucket}") private String bucketName; @@ -72,12 +71,9 @@ private static void verifyValidPageInfo(Pageable pageable) { */ @Transactional public void sendMessage(String userName, MessageRequestDto messageData) throws IOException { - String imageUrl = saveImage(messageData.getImage()); + userService.checkAuth(userName); - if (messageData.getReceiverName().equals(GROUP_EVERYONE)) { - sendMessageToAll(messageData, userName, imageUrl); - return; - } + String imageUrl = saveImage(messageData.getImage()); verifyExistUser(messageData); verifyValidMessageFormat(messageData.getContext()); @@ -96,26 +92,6 @@ private void verifySenderNotEqualReceiver(String userName, MessageRequestDto mes } } - /** - * 로그인한 유저를 제외한 모두에게 메세지를 보냅니다. - * - * @param messageData - * @param userName - * @param imageUrl - */ - @Transactional - public void sendMessageToAll(MessageRequestDto messageData, String userName, String imageUrl) { - verifyValidMessageFormat(messageData.getContext()); - - List userList = userRepository.findAll(); - userList.removeIf(user -> user.getName().equals(userName)); - List messageList = userList.stream() - .map(user -> Message.of(userName, messageData.getReceiverName(), - messageData.getContext(), imageUrl, LocalDateTime.now())) - .toList(); - messageRepository.saveAll(messageList); - } - /** * 수정할 부분 없읍니다! *

@@ -253,7 +229,8 @@ private Page getMessagesSendMe(String userName, PageRequest pageRequest } private void verifyExistUser(MessageRequestDto messageData) { - if (!userRepository.existsByName(messageData.getReceiverName())) { + if (!userRepository.existsByName(messageData.getReceiverName()) + && !userService.checkGroupExists(messageData.getReceiverName())) { throw ExceptionStatus.NOT_FOUND_USER.asGreetingException(); } } diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java index 22eb210..ac95ba1 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/user/service/UserService.java @@ -110,10 +110,15 @@ public void checkAuth(String name) { // 쿠키에 유저이름이 있지만 데이터베이스와 일치하지 않는 경우 if (!userRepository.existsByName(name)) { + System.out.println("다람쥐"); throw ExceptionStatus.NOT_FOUND_USER.asGreetingException(); } } + public boolean checkGroupExists(String groupName) { + return groupNames.contains(groupName); + } + private void verifyNameIsNumericOrAlphabet(String name) { if (name == null || !name.matches("^[a-zA-Z0-9]+$")) { throw ExceptionStatus.INVALID_NAME.asGreetingException(); From fa1188fbd84f56ae555a66498ea95105fec2abec Mon Sep 17 00:00:00 2001 From: lamodadite Date: Fri, 24 Jan 2025 20:47:07 +0900 Subject: [PATCH 089/104] =?UTF-8?q?REFACTOR=20:=20verify=ED=95=98=EB=8A=94?= =?UTF-8?q?=20=EB=A9=94=EC=84=9C=EB=93=9C=EB=93=A4=20=EC=A0=84=EB=B6=80=20?= =?UTF-8?q?=EC=95=84=EB=9E=98=EB=A1=9C=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../message/service/MessageService.java | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java index 2791698..608fab4 100644 --- a/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java +++ b/v2/backend/greetingCard/src/main/java/com/cabi/greetingCard/message/service/MessageService.java @@ -43,25 +43,6 @@ public class MessageService { @Value("${cloud.aws.s3.bucket}") private String bucketName; - private static void verifyValidMessageFormat(String context) { - if (context.isEmpty() - || context.length() > MESSAGE_LENGTH_LIMIT) { - throw ExceptionStatus.INVALID_FORMAT_MESSAGE.asGreetingException(); - } - } - - private static void verifyUserAuthorized(String userName, Message message) { - if (!message.getSenderName().equals(userName)) { - throw ExceptionStatus.UNAUTHORIZED.asGreetingException(); - } - } - - private static void verifyValidPageInfo(Pageable pageable) { - if (pageable.getPageNumber() < 0 || pageable.getPageSize() <= 0) { - throw ExceptionStatus.INVALID_QUERYSTRING.asGreetingException(); - } - } - /** * 메세지 보내기 * @@ -86,12 +67,6 @@ public void sendMessage(String userName, MessageRequestDto messageData) throws I messageRepository.save(message); } - private void verifySenderNotEqualReceiver(String userName, MessageRequestDto messageData) { - if (userName.equals(messageData.getReceiverName())) { - throw ExceptionStatus.SENDER_EQUAL_RECEIVER.asGreetingException(); - } - } - /** * 수정할 부분 없읍니다! *

@@ -234,5 +209,30 @@ private void verifyExistUser(MessageRequestDto messageData) { throw ExceptionStatus.NOT_FOUND_USER.asGreetingException(); } } + + private void verifyValidMessageFormat(String context) { + if (context.isEmpty() + || context.length() > MESSAGE_LENGTH_LIMIT) { + throw ExceptionStatus.INVALID_FORMAT_MESSAGE.asGreetingException(); + } + } + + private void verifyUserAuthorized(String userName, Message message) { + if (!message.getSenderName().equals(userName)) { + throw ExceptionStatus.UNAUTHORIZED.asGreetingException(); + } + } + + private void verifyValidPageInfo(Pageable pageable) { + if (pageable.getPageNumber() < 0 || pageable.getPageSize() <= 0) { + throw ExceptionStatus.INVALID_QUERYSTRING.asGreetingException(); + } + } + + private void verifySenderNotEqualReceiver(String userName, MessageRequestDto messageData) { + if (userName.equals(messageData.getReceiverName())) { + throw ExceptionStatus.SENDER_EQUAL_RECEIVER.asGreetingException(); + } + } } From 9201b392ae97f1d7611bbecd9fba83a7505efe0a Mon Sep 17 00:00:00 2001 From: Jihyun Kim Date: Sat, 25 Jan 2025 22:43:03 +0900 Subject: [PATCH 090/104] =?UTF-8?q?[FE]=20FIX:=20=EC=83=89=EC=83=81=20?= =?UTF-8?q?=ED=98=95=ED=83=9C=20=ED=86=B5=EC=9D=BC=20=EB=B0=8F=20=EC=A1=B0?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/components/ImageUploader.tsx | 6 +++--- .../src/components/ListPage/ListTopNav.tsx | 2 +- .../ListPage/section/CategoryButtons.tsx | 8 +++++--- .../components/ListPage/section/MessageBox.tsx | 4 ++-- .../ListPage/section/MessageList.tsx | 4 ++-- .../src/components/SearchInputField.tsx | 12 +++++++----- v2/frontend/src/components/UserInputField.tsx | 6 ++++-- v2/frontend/src/index.css | 5 +++-- v2/frontend/src/pages/LoginPage.tsx | 8 ++++---- v2/frontend/src/pages/RegisterPage.tsx | 8 ++++---- v2/frontend/src/pages/SendPage.tsx | 18 +++++++++--------- 11 files changed, 44 insertions(+), 37 deletions(-) diff --git a/v2/frontend/src/components/ImageUploader.tsx b/v2/frontend/src/components/ImageUploader.tsx index 5b7e3ef..85e5baa 100644 --- a/v2/frontend/src/components/ImageUploader.tsx +++ b/v2/frontend/src/components/ImageUploader.tsx @@ -68,9 +68,9 @@ const FileInputStyled = styled.input` &::file-selector-button { width: 70px; height: 25px; - border: 1px solid #ccc; + border: 1px solid var(--ref-gray-400); border-radius: 4px; - background: white; + background: var(--ref-white); cursor: pointer; } `; @@ -79,7 +79,7 @@ const DeleteButtonStyled = styled.button` width: 25px; height: 100%; padding-bottom: 3px; - color: red; + color: var(--ref-red-100); font-size: 14px; font-weight: 500; cursor: pointer; diff --git a/v2/frontend/src/components/ListPage/ListTopNav.tsx b/v2/frontend/src/components/ListPage/ListTopNav.tsx index 74c36e1..87b2cf1 100644 --- a/v2/frontend/src/components/ListPage/ListTopNav.tsx +++ b/v2/frontend/src/components/ListPage/ListTopNav.tsx @@ -15,7 +15,7 @@ const LinkWrapperStyled = styled.div` width: 100%; display: flex; justify-content: flex-end; - color: #9747ff; + color: var(--ref-purple-500); font-size: 0.875rem; `; diff --git a/v2/frontend/src/components/ListPage/section/CategoryButtons.tsx b/v2/frontend/src/components/ListPage/section/CategoryButtons.tsx index a87fe8a..a13bd41 100644 --- a/v2/frontend/src/components/ListPage/section/CategoryButtons.tsx +++ b/v2/frontend/src/components/ListPage/section/CategoryButtons.tsx @@ -33,7 +33,7 @@ const CategoryButtons = ({ }; const ButtonWrapper = styled.div` - background-color: #d0d0d0; + background-color: var(--ref-gray-300); width: fit-content; height: fit-content; border-radius: 10px; @@ -41,12 +41,14 @@ const ButtonWrapper = styled.div` `; const ButtonStyled = styled.button<{ $isActived: boolean }>` - background-color: ${(props) => (props.$isActived ? `#9747FF` : `#d0d0d0`)}; + background-color: ${(props) => + props.$isActived ? `var(--ref-purple-500)` : `var(--ref-gray-300)`}; width: fit-content; height: 30px; border-radius: 10px; padding: 5px 10px; - color: ${(props) => (props.$isActived ? `#ffffff` : `#000000`)}; + color: ${(props) => + props.$isActived ? `var(--ref-white)` : `var(--ref-black)`}; font-size: 14px; font-weight: 600; `; diff --git a/v2/frontend/src/components/ListPage/section/MessageBox.tsx b/v2/frontend/src/components/ListPage/section/MessageBox.tsx index 1dc36c3..872d8a5 100644 --- a/v2/frontend/src/components/ListPage/section/MessageBox.tsx +++ b/v2/frontend/src/components/ListPage/section/MessageBox.tsx @@ -43,7 +43,7 @@ const MessageBoxStyled = styled.div` display: flex; align-items: center; padding: 1rem; - border: 1px solid #ddd; + border: 1px solid var(--ref-gray-300); border-radius: 28px; margin-bottom: 1rem; `; @@ -57,7 +57,7 @@ const ImgStyled = styled.img` const SenderReceiverStyled = styled.div` width: 100%; - color: #666666; + color: var(--ref-gray-500); font-size: 14px; `; diff --git a/v2/frontend/src/components/ListPage/section/MessageList.tsx b/v2/frontend/src/components/ListPage/section/MessageList.tsx index b07cf23..71fde55 100644 --- a/v2/frontend/src/components/ListPage/section/MessageList.tsx +++ b/v2/frontend/src/components/ListPage/section/MessageList.tsx @@ -52,7 +52,7 @@ const MessageBoxStyled = styled.div` const EmptyState = styled.div` text-align: center; padding: 2rem; - color: #666; + color: var(--ref-gray-600); `; const ModalOverlay = styled.div` @@ -61,7 +61,7 @@ const ModalOverlay = styled.div` left: 0; width: 100%; height: 100%; - background: rgba(0, 0, 0, 0.7); + background: var(--ref-black); display: flex; align-items: center; justify-content: center; diff --git a/v2/frontend/src/components/SearchInputField.tsx b/v2/frontend/src/components/SearchInputField.tsx index aad9756..9fe5188 100644 --- a/v2/frontend/src/components/SearchInputField.tsx +++ b/v2/frontend/src/components/SearchInputField.tsx @@ -91,12 +91,14 @@ const SearchWrapperStyled = styled.div` const SearchInputFieldStyled = styled.input<{ $isFocus: boolean }>` width: 100%; height: 40px; - background-color: #ffffff; + background-color: var(--ref-white); border-radius: 8px; text-align: left; padding: 10px; border-radius: 8px; - border: 2px solid ${({ $isFocus }) => ($isFocus ? "#9747ff" : "#ffffff")}; + border: 2px solid + ${({ $isFocus }) => + $isFocus ? "var(--ref-purple-500)" : "var(--ref-white)"}; text-align: left; `; @@ -105,7 +107,7 @@ const SearchResultStyled = styled.div` position: absolute; z-index: 1000; border-radius: 8px; - background-color: #ffffff; + background-color: var(--ref-white); `; const SearchUlStyled = styled.ul` @@ -123,8 +125,8 @@ const SearchLiStyled = styled.li` border-radius: 8px; cursor: pointer; &:hover { - background-color: #9747ff; - color: #ffffff; + background-color: var(--ref-purple-500); + color: var(--ref-white); } `; diff --git a/v2/frontend/src/components/UserInputField.tsx b/v2/frontend/src/components/UserInputField.tsx index 8315ed9..b524e55 100644 --- a/v2/frontend/src/components/UserInputField.tsx +++ b/v2/frontend/src/components/UserInputField.tsx @@ -51,13 +51,15 @@ const LoginInputFieldStyled = styled.input<{ $isFocus: boolean }>` padding: 0.5rem 0.8rem; text-align: left; font-size: 1rem; - border: 2px solid ${({ $isFocus }) => ($isFocus ? "#9747ff" : "#D7D7D7")}; + border: 2px solid + ${({ $isFocus }) => + $isFocus ? "var(--ref-purple-500)" : "var(--ref-gray-300)"}; border-radius: 4px; outline: none; transition: border-color 0.3s; &::placeholder { - color: #858486; + color: var(--ref-gray-500); } `; diff --git a/v2/frontend/src/index.css b/v2/frontend/src/index.css index 1f7192f..9cad229 100644 --- a/v2/frontend/src/index.css +++ b/v2/frontend/src/index.css @@ -16,8 +16,9 @@ --ref-black: #181818; /* red */ - --ref-red-100: #ff4e4e; - --ref-red-200: #e54646; + --ref-red-100: #ff0000; + --ref-red-200: #ff4e4e; + --ref-red-300: #e54646; /* orange */ --ref-orange-100: #ff8b5b; diff --git a/v2/frontend/src/pages/LoginPage.tsx b/v2/frontend/src/pages/LoginPage.tsx index df82f1b..9ebde7b 100644 --- a/v2/frontend/src/pages/LoginPage.tsx +++ b/v2/frontend/src/pages/LoginPage.tsx @@ -62,7 +62,7 @@ const LoginPageStyled = styled.div` align-items: center; width: 100%; height: 100vh; - background-color: #f9f9f9; + background-color: var(--ref-gray-100); `; const LoginTitleStyled = styled.div` @@ -75,7 +75,7 @@ const LoginTitleStyled = styled.div` const LinkStyled = styled(Link)` font-size: 1rem; - color: #9747ff; + color: var(--ref-purple-500); line-height: 2rem; margin-top: 1rem; `; @@ -84,8 +84,8 @@ const LoginButtonStyled = styled.button` width: 350px; height: 54px; padding: 0.8rem 1rem; - background-color: #9747ff; - color: #ffffff; + background-color: var(--ref-purple-500); + color: var(--ref-white); font-size: 1rem; border: none; border-radius: 4px; diff --git a/v2/frontend/src/pages/RegisterPage.tsx b/v2/frontend/src/pages/RegisterPage.tsx index 06699ca..e9a035b 100644 --- a/v2/frontend/src/pages/RegisterPage.tsx +++ b/v2/frontend/src/pages/RegisterPage.tsx @@ -61,7 +61,7 @@ const RegisterPageStyled = styled.div` align-items: center; width: 100%; height: 100vh; - background-color: #f9f9f9; + background-color: var(--ref-gray-100); `; const RegisterTitleStyled = styled.div` @@ -74,7 +74,7 @@ const RegisterTitleStyled = styled.div` const RegisterTextStyled = styled.div` font-size: 1rem; - color: #858486; + color: var(--ref-gray-500); line-height: 2rem; margin-top: 1rem; `; @@ -83,8 +83,8 @@ const RegisterButtonStyled = styled.button` width: 350px; height: 54px; padding: 0.8rem 1rem; - background-color: #9747ff; - color: #ffffff; + background-color: var(--ref-purple-500); + color: var(--ref-white); font-size: 1rem; border: none; border-radius: 4px; diff --git a/v2/frontend/src/pages/SendPage.tsx b/v2/frontend/src/pages/SendPage.tsx index 101b9bd..a2d47ab 100644 --- a/v2/frontend/src/pages/SendPage.tsx +++ b/v2/frontend/src/pages/SendPage.tsx @@ -131,9 +131,9 @@ const LogoutWrapperStyled = styled.div` button { width: 100px; height: 30px; - color: #999999; + color: var(--ref-gray-500); font-size: 0.875rem; - border: 1px solid #ffffff; + border: 1px solid var(--ref-white); border-radius: 4px; cursor: pointer; } @@ -144,7 +144,7 @@ const LinkWrapperStyled = styled.div` height: 50px; display: flex; justify-content: flex-end; - color: #9747ff; + color: var(--ref-purple-500); font-size: 0.875rem; `; @@ -173,7 +173,7 @@ const FormWrapperStyled = styled.div` flex-direction: column; justify-content: flex-start; align-items: center; - background-color: #f5f5f5; + background-color: var(--ref-gray-100); border-radius: 10px; padding: 30px 20px; gap: 20px; @@ -184,10 +184,10 @@ const FormContainerStyled = styled.div` `; const FormSubTitleStyled = styled.h3` font-size: 0.875rem; - color: #7b7b7b; + color: var(--ref-gray-500); margin-bottom: 10px; .red { - color: #ff4e4e; + color: var(--ref-red-200); } `; @@ -227,10 +227,10 @@ const FormButtonStyled = styled.button` width: 100px; height: 30px; font-size: 0.875rem; - background-color: #9747ff; - color: #ffffff; + background-color: var(--ref-purple-500); + color: var(--ref-white); font-weight: 700; - border: 1px solid #ffffff; + border: 1px solid var(--ref-white); border-radius: 4px; cursor: pointer; `; From cde2dcdf08a9556d1d6662bd636aca249024edc3 Mon Sep 17 00:00:00 2001 From: JunSeong Date: Mon, 27 Jan 2025 10:09:36 +0900 Subject: [PATCH 091/104] =?UTF-8?q?[FE]=20FIX:=20route=20useEffect=20?= =?UTF-8?q?=EB=B2=84=EA=B7=B8=20=ED=94=BD=EC=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/components/PrivateRoute.tsx | 2 +- v2/frontend/src/components/PublicRoute.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/v2/frontend/src/components/PrivateRoute.tsx b/v2/frontend/src/components/PrivateRoute.tsx index ac75b2d..15efbb6 100644 --- a/v2/frontend/src/components/PrivateRoute.tsx +++ b/v2/frontend/src/components/PrivateRoute.tsx @@ -25,7 +25,7 @@ const PrivateRoute = ({ }; checkAuthStatus(); - }, []); + }, [location.pathname]); if (isLoading) { return

Loading...
; diff --git a/v2/frontend/src/components/PublicRoute.tsx b/v2/frontend/src/components/PublicRoute.tsx index cb54292..f01221c 100644 --- a/v2/frontend/src/components/PublicRoute.tsx +++ b/v2/frontend/src/components/PublicRoute.tsx @@ -25,7 +25,7 @@ const PublicRoute = ({ }; checkAuthStatus(); - }, []); + }, [location.pathname]); if (isLoading) { return
Loading...
; From 485f5269bde6378e19ca33bf22a02472c1f56b2d Mon Sep 17 00:00:00 2001 From: JunSeong Date: Mon, 27 Jan 2025 10:11:06 +0900 Subject: [PATCH 092/104] =?UTF-8?q?[FE]=20FIX:=20messageId=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=ED=8D=BC=ED=8B=B0=20=EB=B3=80=EA=B2=BD=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/components/ListPage/ListInterfaces.tsx | 2 +- v2/frontend/src/components/ListPage/section/MessageList.tsx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/v2/frontend/src/components/ListPage/ListInterfaces.tsx b/v2/frontend/src/components/ListPage/ListInterfaces.tsx index f59557b..91a89fc 100644 --- a/v2/frontend/src/components/ListPage/ListInterfaces.tsx +++ b/v2/frontend/src/components/ListPage/ListInterfaces.tsx @@ -1,5 +1,5 @@ interface Message { - messageId: string; + id: number; senderName: string; receiverName: string; context: string; diff --git a/v2/frontend/src/components/ListPage/section/MessageList.tsx b/v2/frontend/src/components/ListPage/section/MessageList.tsx index 71fde55..64dc65b 100644 --- a/v2/frontend/src/components/ListPage/section/MessageList.tsx +++ b/v2/frontend/src/components/ListPage/section/MessageList.tsx @@ -21,7 +21,8 @@ const MessageList = ({ items, isLoading }: MessageListProps) => { {items.map((item) => ( Date: Mon, 27 Jan 2025 10:11:43 +0900 Subject: [PATCH 093/104] =?UTF-8?q?[FE]=20FEAT:=20=EB=8D=94=EB=B3=B4?= =?UTF-8?q?=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20=EC=8A=A4=ED=83=80=EC=9D=BC?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ListPage/section/MoreButtons.tsx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/v2/frontend/src/components/ListPage/section/MoreButtons.tsx b/v2/frontend/src/components/ListPage/section/MoreButtons.tsx index 895a0c2..73ce140 100644 --- a/v2/frontend/src/components/ListPage/section/MoreButtons.tsx +++ b/v2/frontend/src/components/ListPage/section/MoreButtons.tsx @@ -16,6 +16,24 @@ const MoreButtons = ({ fetchItems, isLoading, isLast }: MoreButtonsProps) => { const MoreButtonStyled = styled.button<{ $isLast: boolean }>` display: ${(props) => (props.$isLast ? "none" : "block")}; + width: 100%; + padding: 12px; + margin: 20px 0; + border-radius: 8px; + background: #9747ff; + font-size: 16px; + font-weight: 500; + color: white; + cursor: pointer; + transition: all 0.2s ease; + + &:hover { + background: #8010ff; + } + + &:active { + transform: scale(0.98); + } `; export default MoreButtons; From b3646742f62fd3896efe05956e66f91ab21cccff Mon Sep 17 00:00:00 2001 From: JunSeong Date: Mon, 27 Jan 2025 10:12:20 +0900 Subject: [PATCH 094/104] =?UTF-8?q?[FE]=20FEAT:=20=EB=A9=94=EC=8B=9C?= =?UTF-8?q?=EC=A7=80=20=EC=88=98=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ListPage/section/MessageBox.tsx | 103 ++++++++++++++++-- 1 file changed, 95 insertions(+), 8 deletions(-) diff --git a/v2/frontend/src/components/ListPage/section/MessageBox.tsx b/v2/frontend/src/components/ListPage/section/MessageBox.tsx index 872d8a5..f95f1fa 100644 --- a/v2/frontend/src/components/ListPage/section/MessageBox.tsx +++ b/v2/frontend/src/components/ListPage/section/MessageBox.tsx @@ -1,6 +1,9 @@ +import { useState } from "react"; import { styled } from "styled-components"; +import { updateMessage } from "../../../api/messages"; interface MessageBoxProps { + messageId: number; senderName: string; receiverName: string; context: string; @@ -10,6 +13,7 @@ interface MessageBoxProps { } const MessageBox = ({ + messageId, senderName, receiverName, context, @@ -17,23 +21,72 @@ const MessageBox = ({ setShowModal, setModalImgUrl, }: MessageBoxProps) => { + const [isEditing, setIsEditing] = useState(false); + const [inputValue, setInputValue] = useState(context); + + const handleEdit = async () => { + if (isEditing) { + if (inputValue !== context) { + try { + const res = await updateMessage(messageId, { context: inputValue }); + // setContext(res.data.message); + } catch (error) { + console.error("Failed to update message:", error); + setInputValue(context); + return; + } + } + } + setIsEditing(!isEditing); + }; + + const handleChange = (e: React.ChangeEvent) => { + setInputValue(e.target.value); + }; + return ( <> { setShowModal(true); setModalImgUrl(imageUrl); }} /> -
+ from {senderName} to {receiverName} - {context} -
+ + {/* 내가 작성한 메시지인지 어떻게 알지? */} + {isEditing ? ( + + ) : ( + {context} + )} + + + {isEditing ? "완료" : "수정"} + + {isEditing && ( + { + setInputValue(context); + setIsEditing(false); + }} + > + 취소 + + )} + + +
); @@ -52,7 +105,11 @@ const ImgStyled = styled.img` width: 100px; height: 100px; border-radius: 50%; - padding: 20px; + margin: 10px 30px 10px 10px; +`; + +const TextStyled = styled.div` + flex-grow: 1; `; const SenderReceiverStyled = styled.div` @@ -61,12 +118,42 @@ const SenderReceiverStyled = styled.div` font-size: 14px; `; +const ContextWrapperStyled = styled.div` + display: flex; + justify-content: space-between; +`; + const ContextStyled = styled.div` - width: 100%; font-size: 20px; font-weight: 700; - padding-top: 8px; - padding-bottom: 24px; + padding-top: 4px; +`; + +const InputStyled = styled.input` + font-size: 20px; + font-weight: 700; + padding: 8px; + border: 1px solid #ddd; + border-radius: 8px; + width: 100%; + margin-right: 10px; + text-align: left; +`; + +const ButtonGroupStyled = styled.div` + display: flex; + gap: 8px; +`; + +const ButtonStyled = styled.button` + background-color: #f0f0f0; + width: 60px; + height: 36px; + border: none; + border-radius: 10px; + padding: 6px 12px; + font-size: 14px; + color: #666666; `; export default MessageBox; From 96069f5d20694598c201a3d4cd08f36d1c7b37bb Mon Sep 17 00:00:00 2001 From: JunSeong Date: Mon, 27 Jan 2025 10:21:23 +0900 Subject: [PATCH 095/104] =?UTF-8?q?[FE]=20FIX:=20=EC=B7=A8=EC=86=8C=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/ListPage/section/MessageBox.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/v2/frontend/src/components/ListPage/section/MessageBox.tsx b/v2/frontend/src/components/ListPage/section/MessageBox.tsx index f95f1fa..87bdfc2 100644 --- a/v2/frontend/src/components/ListPage/section/MessageBox.tsx +++ b/v2/frontend/src/components/ListPage/section/MessageBox.tsx @@ -44,6 +44,11 @@ const MessageBox = ({ setInputValue(e.target.value); }; + const handleCancel = () => { + setInputValue(context); + setIsEditing(false); + }; + return ( <> @@ -75,14 +80,7 @@ const MessageBox = ({ {isEditing ? "완료" : "수정"} {isEditing && ( - { - setInputValue(context); - setIsEditing(false); - }} - > - 취소 - + 취소 )} From 85862179330951950f27ddb08a864c82a6b60986 Mon Sep 17 00:00:00 2001 From: jiminChoi Date: Tue, 28 Jan 2025 15:38:45 +0900 Subject: [PATCH 096/104] =?UTF-8?q?[FE]=20FIX:=20everyone=20=EC=A0=84?= =?UTF-8?q?=EC=86=A1=20=EC=BF=BC=EB=A6=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/components/SearchInputField.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/frontend/src/components/SearchInputField.tsx b/v2/frontend/src/components/SearchInputField.tsx index 9fe5188..e933e9c 100644 --- a/v2/frontend/src/components/SearchInputField.tsx +++ b/v2/frontend/src/components/SearchInputField.tsx @@ -21,7 +21,7 @@ const SearchInputField = ({ } try { if (inputValue[0] === "@") { - const res = await searchGroup({ input: inputValue }); + const res = await searchGroup({ input: "everyone" }); setSearchResult(res.data); } else { const res = await searchName({ input: inputValue }); From b8dcb43b3ba5234f03f19adfef7408e9faf88068 Mon Sep 17 00:00:00 2001 From: jiminChoi Date: Tue, 28 Jan 2025 16:37:18 +0900 Subject: [PATCH 097/104] =?UTF-8?q?[FE]=20FIX:=20searchResult=20->=20searc?= =?UTF-8?q?hList=20=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/SearchInputField.tsx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/v2/frontend/src/components/SearchInputField.tsx b/v2/frontend/src/components/SearchInputField.tsx index e933e9c..3d79ff8 100644 --- a/v2/frontend/src/components/SearchInputField.tsx +++ b/v2/frontend/src/components/SearchInputField.tsx @@ -8,28 +8,28 @@ const SearchInputField = ({ setSearchInputText: (searchTerm: string) => void; }) => { const [isFocused, setIsFocused] = useState(false); - const [searchResult, setSearchResult] = useState<{ names: string[] }>({ - names: [], - }); + const [searchList, setSearchList] = useState([]); const [inputValue, setInputValue] = useState(""); useEffect(() => { const debounceTimer = setTimeout(async () => { if (inputValue == "") { - setSearchResult({ names: [] }); + setSearchList([]); return; } try { if (inputValue[0] === "@") { - const res = await searchGroup({ input: "everyone" }); - setSearchResult(res.data); + const searchTerm = inputValue.slice(1).trim(); + const res = await searchGroup({ input: searchTerm }); + setSearchList(res.data.groupNames); } else { const res = await searchName({ input: inputValue }); - setSearchResult(res.data); + console.log(res.data.names); + setSearchList(res.data.names); } } catch (error: any) { alert(error.response.data.message); - setSearchResult({ names: [] }); + setSearchList([]); } }, 500); @@ -64,9 +64,9 @@ const SearchInputField = ({ /> {isFocused && ( - {searchResult.names?.length > 0 && ( + {searchList.length > 0 && ( - {searchResult.names.map((result) => ( + {searchList.map((result) => ( setSearchName(result)} From 1c273cea80d8cf224873d6b93dcac4da8e1122e0 Mon Sep 17 00:00:00 2001 From: JunSeong Date: Wed, 29 Jan 2025 17:26:44 +0900 Subject: [PATCH 098/104] =?UTF-8?q?[FE]=20FEAT:=20=EB=8D=95=EB=8B=B4=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/assets/images/close-icon.svg | 5 + .../components/ListPage/ListInterfaces.tsx | 1 + .../ListPage/section/MessageBox.tsx | 94 +---------- .../ListPage/section/MessageBoxEditable.tsx | 155 ++++++++++++++++++ .../ListPage/section/MessageList.tsx | 49 ++++-- 5 files changed, 206 insertions(+), 98 deletions(-) create mode 100644 v2/frontend/src/assets/images/close-icon.svg create mode 100644 v2/frontend/src/components/ListPage/section/MessageBoxEditable.tsx diff --git a/v2/frontend/src/assets/images/close-icon.svg b/v2/frontend/src/assets/images/close-icon.svg new file mode 100644 index 0000000..21b08b0 --- /dev/null +++ b/v2/frontend/src/assets/images/close-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/v2/frontend/src/components/ListPage/ListInterfaces.tsx b/v2/frontend/src/components/ListPage/ListInterfaces.tsx index 91a89fc..3916a7d 100644 --- a/v2/frontend/src/components/ListPage/ListInterfaces.tsx +++ b/v2/frontend/src/components/ListPage/ListInterfaces.tsx @@ -4,6 +4,7 @@ interface Message { receiverName: string; context: string; imageUrl: string; + mine: boolean; } export default Message; diff --git a/v2/frontend/src/components/ListPage/section/MessageBox.tsx b/v2/frontend/src/components/ListPage/section/MessageBox.tsx index 87bdfc2..ba4f141 100644 --- a/v2/frontend/src/components/ListPage/section/MessageBox.tsx +++ b/v2/frontend/src/components/ListPage/section/MessageBox.tsx @@ -1,61 +1,26 @@ -import { useState } from "react"; import { styled } from "styled-components"; -import { updateMessage } from "../../../api/messages"; +import Message from "../ListInterfaces"; interface MessageBoxProps { - messageId: number; - senderName: string; - receiverName: string; - context: string; - imageUrl: string; + item: Message; setShowModal: (value: boolean) => void; setModalImgUrl: (value: string) => void; } const MessageBox = ({ - messageId, - senderName, - receiverName, - context, - imageUrl, + item: { senderName, receiverName, context, imageUrl }, setShowModal, setModalImgUrl, }: MessageBoxProps) => { - const [isEditing, setIsEditing] = useState(false); - const [inputValue, setInputValue] = useState(context); - - const handleEdit = async () => { - if (isEditing) { - if (inputValue !== context) { - try { - const res = await updateMessage(messageId, { context: inputValue }); - // setContext(res.data.message); - } catch (error) { - console.error("Failed to update message:", error); - setInputValue(context); - return; - } - } - } - setIsEditing(!isEditing); - }; - - const handleChange = (e: React.ChangeEvent) => { - setInputValue(e.target.value); - }; - - const handleCancel = () => { - setInputValue(context); - setIsEditing(false); - }; - return ( <> { + if (imageUrl === "") return; setShowModal(true); setModalImgUrl(imageUrl); }} @@ -65,24 +30,7 @@ const MessageBox = ({ from {senderName} to {receiverName} - {/* 내가 작성한 메시지인지 어떻게 알지? */} - {isEditing ? ( - - ) : ( - {context} - )} - - - {isEditing ? "완료" : "수정"} - - {isEditing && ( - 취소 - )} - + {context} @@ -99,11 +47,12 @@ const MessageBoxStyled = styled.div` margin-bottom: 1rem; `; -const ImgStyled = styled.img` +const ImgStyled = styled.img<{ $hasUrl: boolean }>` width: 100px; height: 100px; border-radius: 50%; margin: 10px 30px 10px 10px; + ${(props) => props.$hasUrl && "cursor: pointer;"} `; const TextStyled = styled.div` @@ -127,31 +76,4 @@ const ContextStyled = styled.div` padding-top: 4px; `; -const InputStyled = styled.input` - font-size: 20px; - font-weight: 700; - padding: 8px; - border: 1px solid #ddd; - border-radius: 8px; - width: 100%; - margin-right: 10px; - text-align: left; -`; - -const ButtonGroupStyled = styled.div` - display: flex; - gap: 8px; -`; - -const ButtonStyled = styled.button` - background-color: #f0f0f0; - width: 60px; - height: 36px; - border: none; - border-radius: 10px; - padding: 6px 12px; - font-size: 14px; - color: #666666; -`; - export default MessageBox; diff --git a/v2/frontend/src/components/ListPage/section/MessageBoxEditable.tsx b/v2/frontend/src/components/ListPage/section/MessageBoxEditable.tsx new file mode 100644 index 0000000..17c05fb --- /dev/null +++ b/v2/frontend/src/components/ListPage/section/MessageBoxEditable.tsx @@ -0,0 +1,155 @@ +import { useState } from "react"; +import styled from "styled-components"; +import { updateMessage } from "../../../api/messages"; +import Message from "../ListInterfaces"; + +interface MessageBoxProps { + item: Message; + setShowModal: (value: boolean) => void; + setModalImgUrl: (value: string) => void; +} + +const MessageBoxEditable = ({ + item: { id: messageId, senderName, receiverName, context, imageUrl }, + setShowModal, + setModalImgUrl, +}: MessageBoxProps) => { + const [isEditing, setIsEditing] = useState(false); + const [inputValue, setInputValue] = useState(context); + const [contextValue, setContextValue] = useState(context); + + const handleEdit = async () => { + if (isEditing) { + if (inputValue !== context) { + try { + const res = await updateMessage(messageId, { context: inputValue }); + if (res.status === 200) { + setContextValue(inputValue); + } + } catch (error) { + console.error("Failed to update message:", error); + setInputValue(contextValue); + return; + } + } + } + setIsEditing(!isEditing); + }; + + const handleChange = (e: React.ChangeEvent) => { + setInputValue(e.target.value); + }; + + const handleCancel = () => { + setInputValue(contextValue); + setIsEditing(false); + }; + + return ( + <> + + { + if (imageUrl === "") return; + setShowModal(true); + setModalImgUrl(imageUrl); + }} + /> + + + from {senderName} to {receiverName} + + + {isEditing ? ( + + ) : ( + {contextValue} + )} + + + {isEditing ? "완료" : "수정"} + + {isEditing && ( + 취소 + )} + + + + + + ); +}; + +const MessageBoxStyled = styled.div` + display: flex; + align-items: center; + padding: 1rem; + border: 1px solid var(--ref-gray-300); + border-radius: 28px; + margin-bottom: 1rem; +`; + +const ImgStyled = styled.img<{ $hasUrl: boolean }>` + width: 100px; + height: 100px; + border-radius: 50%; + margin: 10px 30px 10px 10px; + ${(props) => props.$hasUrl && "cursor: pointer;"} +`; + +const TextStyled = styled.div` + flex-grow: 1; +`; + +const SenderReceiverStyled = styled.div` + width: 100%; + color: var(--ref-gray-500); + font-size: 14px; +`; + +const ContextWrapperStyled = styled.div` + display: flex; + justify-content: space-between; +`; + +const ContextStyled = styled.div` + font-size: 20px; + font-weight: 700; + padding-top: 4px; +`; + +const InputStyled = styled.input` + font-size: 20px; + font-weight: 700; + padding: 8px; + border: 1px solid #ddd; + border-radius: 8px; + width: 100%; + margin-right: 10px; + text-align: left; +`; + +const ButtonGroupStyled = styled.div` + display: flex; + gap: 8px; +`; + +const ButtonStyled = styled.button` + background-color: #f0f0f0; + width: 60px; + height: 36px; + border: none; + border-radius: 10px; + padding: 6px 12px; + font-size: 14px; + color: #666666; +`; + +export default MessageBoxEditable; diff --git a/v2/frontend/src/components/ListPage/section/MessageList.tsx b/v2/frontend/src/components/ListPage/section/MessageList.tsx index 64dc65b..2b12f75 100644 --- a/v2/frontend/src/components/ListPage/section/MessageList.tsx +++ b/v2/frontend/src/components/ListPage/section/MessageList.tsx @@ -2,6 +2,8 @@ import { useState } from "react"; import styled from "styled-components"; import Message from "../ListInterfaces"; import MessageBox from "./MessageBox"; +import MessageBoxEditable from "./MessageBoxEditable"; +import { ReactComponent as CloseIcon } from "../../../assets/images/close-icon.svg"; interface MessageListProps { items: Message[]; @@ -19,18 +21,23 @@ const MessageList = ({ items, isLoading }: MessageListProps) => { return ( <> - {items.map((item) => ( - - ))} + {items.map((item) => + item.mine ? ( + + ) : ( + + ) + )} {showModal && ( @@ -40,6 +47,9 @@ const MessageList = ({ items, isLoading }: MessageListProps) => { alt="확대된 이미지" onClick={(e) => e.stopPropagation()} /> + setShowModal(false)}> + + )} @@ -76,4 +86,19 @@ const ModalImage = styled.img` object-fit: contain; `; +const CloseButton = styled.button` + position: absolute; + top: 10px; + right: 10px; + background: white; + border: none; + border-radius: 50%; + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; +`; + export default MessageList; From 504f13ef9cf534c0389c243a4ce4c6a762c4cbd7 Mon Sep 17 00:00:00 2001 From: JunSeong Date: Wed, 29 Jan 2025 17:27:16 +0900 Subject: [PATCH 099/104] =?UTF-8?q?[FE]=20FEAT:=20=EB=8D=95=EB=8B=B4=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=B4=88=EA=B8=B0=20=EB=A1=9C?= =?UTF-8?q?=EB=94=A9=EC=9D=84=20=EB=A1=9C=EB=8D=94=EB=A1=9C=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/routes.tsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/v2/frontend/src/routes.tsx b/v2/frontend/src/routes.tsx index ddc8309..6fe13b6 100644 --- a/v2/frontend/src/routes.tsx +++ b/v2/frontend/src/routes.tsx @@ -5,6 +5,8 @@ import ListPage from "./pages/ListPage"; import { ReactElement } from "react"; import PrivateRoute from "./components/PrivateRoute"; import PublicRoute from "./components/PublicRoute"; +import { getMessages } from "./api/messages"; +import { Filter, LIST_SIZE } from "./constant"; enum AccessType { PUBLIC, @@ -15,6 +17,7 @@ export interface RouteInfo { path: string; accessType: AccessType; element: ReactElement; + loader?: () => Promise; } const routesInfo: RouteInfo[] = [ @@ -22,6 +25,19 @@ const routesInfo: RouteInfo[] = [ path: "/", accessType: AccessType.PRIVATE, element: , + loader: async () => { + try { + const res = await getMessages({ + page: 0, + size: LIST_SIZE, + category: Filter.TO_EVERYONE, + }); + return res; + } catch (error) { + console.error("Failed to fetch messages:", error); + return null; + } + }, }, { path: "/login", From 9ab771d3baf30c3b335850d05c81dc05d042165c Mon Sep 17 00:00:00 2001 From: JunSeong Date: Wed, 29 Jan 2025 17:27:47 +0900 Subject: [PATCH 100/104] =?UTF-8?q?[FE]=20FIX:=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EB=B2=84=ED=8A=BC=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/ListPage/section/CategoryButtons.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/v2/frontend/src/components/ListPage/section/CategoryButtons.tsx b/v2/frontend/src/components/ListPage/section/CategoryButtons.tsx index a13bd41..d55f63c 100644 --- a/v2/frontend/src/components/ListPage/section/CategoryButtons.tsx +++ b/v2/frontend/src/components/ListPage/section/CategoryButtons.tsx @@ -1,13 +1,15 @@ import styled from "styled-components"; import { Filter } from "../../../constant"; +interface FilterButtonProps { + currentCategory: Filter; + handleChangedCategory: (category: Filter) => void; +} + const CategoryButtons = ({ currentCategory, handleChangedCategory, -}: { - currentCategory: Filter; - handleChangedCategory: (category: Filter) => void; -}) => { +}: FilterButtonProps) => { return ( Date: Wed, 29 Jan 2025 17:28:27 +0900 Subject: [PATCH 101/104] =?UTF-8?q?[FE]=20FEAT:=20=EB=8D=95=EB=8B=B4=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EB=A1=9C=EB=93=9C=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/ListPage/ListSection.tsx | 54 +++++++++++-------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/v2/frontend/src/components/ListPage/ListSection.tsx b/v2/frontend/src/components/ListPage/ListSection.tsx index 803a7ce..8760649 100644 --- a/v2/frontend/src/components/ListPage/ListSection.tsx +++ b/v2/frontend/src/components/ListPage/ListSection.tsx @@ -1,19 +1,21 @@ -import { useCallback, useEffect, useState } from "react"; +import { useState } from "react"; import { Filter, LIST_SIZE } from "../../constant"; import Message from "./ListInterfaces"; import CategoryButtons from "./section/CategoryButtons"; import MessageList from "./section/MessageList"; import MoreButtons from "./section/MoreButtons"; import { getMessages } from "../../api/messages"; +import { useLoaderData } from "react-router"; const ListSection = () => { - const [items, setItems] = useState([]); - const [nextPage, setNextPage] = useState(0); + const loaderData = useLoaderData(); + const [items, setItems] = useState(loaderData.data.messages); + const [nextPage, setNextPage] = useState(loaderData.data.currentPage + 1); const [isLoading, setIsLoading] = useState(false); const [category, setCategory] = useState(Filter.TO_EVERYONE); const [isLast, setIsLast] = useState(false); - const fetchItems = useCallback(async () => { + const fetchItems = async () => { if (isLast || isLoading) return; try { @@ -24,33 +26,39 @@ const ListSection = () => { category, }); - if (res.data.messages.length === 0) { - setIsLast(true); - return; - } - - setItems((prev) => - nextPage === 0 ? res.data.messages : [...prev, ...res.data.messages] - ); + setItems((prev) => [...prev, ...res.data.messages]); setNextPage(res.data.currentPage + 1); - setIsLast(res.data.currentPage >= res.data.totalLength); + setIsLast(res.data.currentPage + 1 >= res.data.totalLength); } catch (error) { console.error("Failed to fetch messages:", error); } finally { setIsLoading(false); } - }, [category, nextPage]); + }; - const handleChangedCategory = (category: Filter) => { - setCategory(category); - setNextPage(0); - setItems([]); - setIsLast(false); + const fetchCategoryItems = async (newCategory: Filter) => { + try { + setIsLoading(true); + const res = await getMessages({ + page: 0, + size: LIST_SIZE, + category: newCategory, + }); + + setItems(res.data.messages); + setNextPage(res.data.currentPage + 1); + setIsLast(res.data.currentPage + 1 >= res.data.totalLength); + } catch (error) { + console.error("Failed to fetch messages:", error); + } finally { + setIsLoading(false); + } }; - useEffect(() => { - fetchItems(); - }, [category]); + const handleChangedCategory = (newCategory: Filter) => { + setCategory(newCategory); + fetchCategoryItems(newCategory); + }; return ( <> @@ -60,9 +68,9 @@ const ListSection = () => { /> ); From ce0102118c0ab4bb7c50119d516eb2f1c4a67771 Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Fri, 31 Jan 2025 15:37:33 +0900 Subject: [PATCH 102/104] =?UTF-8?q?[FE]=20REMOVE:=20Logout=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EB=A1=9C=EC=A7=81=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/pages/SendPage.tsx | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/v2/frontend/src/pages/SendPage.tsx b/v2/frontend/src/pages/SendPage.tsx index a2d47ab..23a6bb9 100644 --- a/v2/frontend/src/pages/SendPage.tsx +++ b/v2/frontend/src/pages/SendPage.tsx @@ -4,7 +4,6 @@ import SearchInputField from "../components/SearchInputField"; import { Link, useNavigate } from "react-router"; import ImageUploader from "../components/ImageUploader"; import { sendMessage } from "../api/messages"; -import { logout } from "../api/users"; const SendPage = () => { const [searchInputText, setSearchInputText] = useState(""); @@ -52,21 +51,8 @@ const SendPage = () => { } }; - const HandleLogout = async () => { - try { - await logout(); - alert("로그아웃."); - navigate("/login"); - } catch (error) { - alert(error); - } - }; - return ( - - - 덕담 보러 가기 @@ -123,22 +109,6 @@ const WrapperStyled = styled.div` padding: 60px 0; `; -const LogoutWrapperStyled = styled.div` - width: 100%; - display: flex; - justify-content: flex-start; - - button { - width: 100px; - height: 30px; - color: var(--ref-gray-500); - font-size: 0.875rem; - border: 1px solid var(--ref-white); - border-radius: 4px; - cursor: pointer; - } -`; - const LinkWrapperStyled = styled.div` width: 80%; height: 50px; From 001c85d52bf7ab706830b9a0836c87461115100d Mon Sep 17 00:00:00 2001 From: gykoh42 Date: Fri, 31 Jan 2025 15:59:21 +0900 Subject: [PATCH 103/104] =?UTF-8?q?[FE]=20FEAT:=20input=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/pages/LoginPage.tsx | 11 +++++++++-- v2/frontend/src/pages/RegisterPage.tsx | 9 +++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/v2/frontend/src/pages/LoginPage.tsx b/v2/frontend/src/pages/LoginPage.tsx index 9ebde7b..f8f0843 100644 --- a/v2/frontend/src/pages/LoginPage.tsx +++ b/v2/frontend/src/pages/LoginPage.tsx @@ -12,9 +12,16 @@ const LoginPage = () => { const idRegex = /^[A-Za-z0-9]{1,10}$/; const pwRegex = /^(?!.*(.)\1{3})[A-Za-z0-9]+$/; - useEffect(() => {}); - const handleLogin = async () => { + if (!idRegex.test(id)) { + alert("형식에 맞지 않는 아이디입니다."); + return; + } + if (!pwRegex.test(pw)) { + alert("형식에 맞지 않는 비밀번호입니다."); + return; + } + try { const data = { name: id, password: pw }; const response = await login(data); diff --git a/v2/frontend/src/pages/RegisterPage.tsx b/v2/frontend/src/pages/RegisterPage.tsx index e9a035b..ad6717f 100644 --- a/v2/frontend/src/pages/RegisterPage.tsx +++ b/v2/frontend/src/pages/RegisterPage.tsx @@ -13,6 +13,15 @@ const RegisterPage = () => { const pwRegex = /^(?!.*(.)\1{3})[A-Za-z0-9]+$/; const handleRegister = async () => { + if (!idRegex.test(id)) { + alert("형식에 맞지 않는 아이디입니다."); + return; + } + if (!pwRegex.test(pw)) { + alert("형식에 맞지 않는 비밀번호입니다."); + return; + } + try { const data = { name: id, password: pw }; const response = await register(data); From e8567148d15d4161f274453bf7f61439b849b1f7 Mon Sep 17 00:00:00 2001 From: jiminChoi Date: Tue, 18 Feb 2025 12:24:50 +0900 Subject: [PATCH 104/104] =?UTF-8?q?[FE]=20FIX:=20searchResult=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=EB=A1=9C=20=EB=B6=84=EB=A6=AC,=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- v2/frontend/src/components/ImageUploader.tsx | 2 +- .../src/components/SearchInputField.tsx | 48 ++-------------- v2/frontend/src/components/SearchResult.tsx | 56 +++++++++++++++++++ v2/frontend/src/pages/SendPage.tsx | 4 +- 4 files changed, 65 insertions(+), 45 deletions(-) create mode 100644 v2/frontend/src/components/SearchResult.tsx diff --git a/v2/frontend/src/components/ImageUploader.tsx b/v2/frontend/src/components/ImageUploader.tsx index 85e5baa..4ff2dce 100644 --- a/v2/frontend/src/components/ImageUploader.tsx +++ b/v2/frontend/src/components/ImageUploader.tsx @@ -20,7 +20,7 @@ const ImageUploader = ({ if (!inputFile.name.match(/\.(jpg|jpeg|png)$/)) { target.value = ""; - alert("이미지 파일만 업로드 가능합니다."); + alert("jpg, jpeg, png 형식의 파일만 업로드 가능합니다."); return; } diff --git a/v2/frontend/src/components/SearchInputField.tsx b/v2/frontend/src/components/SearchInputField.tsx index 3d79ff8..6b3bd87 100644 --- a/v2/frontend/src/components/SearchInputField.tsx +++ b/v2/frontend/src/components/SearchInputField.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from "react"; import styled from "styled-components"; import { searchGroup, searchName } from "../api/users"; +import SearchResultStyled from "./SearchResult"; const SearchInputField = ({ setSearchInputText, @@ -63,20 +64,11 @@ const SearchInputField = ({ $isFocus={isFocused} /> {isFocused && ( - - {searchList.length > 0 && ( - - {searchList.map((result) => ( - setSearchName(result)} - > - {result} - - ))} - - )} - + + )} @@ -102,32 +94,4 @@ const SearchInputFieldStyled = styled.input<{ $isFocus: boolean }>` text-align: left; `; -const SearchResultStyled = styled.div` - width: 100%; - position: absolute; - z-index: 1000; - border-radius: 8px; - background-color: var(--ref-white); -`; - -const SearchUlStyled = styled.ul` - min-height: 30px; - padding-left: 0px; -`; - -const SearchLiStyled = styled.li` - height: 30px; - display: flex; - justify-content: flex-start; - align-items: center; - padding-left: 10px; - padding-bottom: 3px; - border-radius: 8px; - cursor: pointer; - &:hover { - background-color: var(--ref-purple-500); - color: var(--ref-white); - } -`; - export default SearchInputField; diff --git a/v2/frontend/src/components/SearchResult.tsx b/v2/frontend/src/components/SearchResult.tsx new file mode 100644 index 0000000..353247f --- /dev/null +++ b/v2/frontend/src/components/SearchResult.tsx @@ -0,0 +1,56 @@ +import styled from "styled-components"; + +const SearchResult = ({ + searchList, + setSearchName, +}: { + searchList: string[]; + setSearchName: (value: string) => void; +}) => { + return ( + + {searchList.length > 0 && ( + + {searchList.map((result) => ( + setSearchName(result)} + > + {result} + + ))} + + )} + + ); +}; + +const SearchResultStyled = styled.div` + width: 100%; + position: absolute; + z-index: 1000; + border-radius: 8px; + background-color: var(--ref-white); +`; + +const SearchUlStyled = styled.ul` + min-height: 30px; + padding-left: 0px; +`; + +const SearchLiStyled = styled.li` + height: 30px; + display: flex; + justify-content: flex-start; + align-items: center; + padding-left: 10px; + padding-bottom: 3px; + border-radius: 8px; + cursor: pointer; + &:hover { + background-color: var(--ref-purple-500); + color: var(--ref-white); + } +`; + +export default SearchResult; \ No newline at end of file diff --git a/v2/frontend/src/pages/SendPage.tsx b/v2/frontend/src/pages/SendPage.tsx index 23a6bb9..41a24da 100644 --- a/v2/frontend/src/pages/SendPage.tsx +++ b/v2/frontend/src/pages/SendPage.tsx @@ -15,7 +15,7 @@ const SendPage = () => { const navigate = useNavigate(); const maxLength = 42; - const handleInputChanged = (e: ChangeEvent) => { + const handleChanged = (e: ChangeEvent) => { // 글자 폭에 따른 자동 길이 조절 const currentValue = messageTextAreaRef.current; if (!currentValue) return; @@ -72,7 +72,7 @@ const SendPage = () => { setIsFocused(true)}