From bdc99c7883c1ffac3d93ba9c87339384ed683ef5 Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 3 Sep 2025 02:03:37 +0000 Subject: [PATCH] =?UTF-8?q?[#37]=20=ED=85=8C=EB=A7=88=20=EC=8A=A4=ED=82=A4?= =?UTF-8?q?=EB=A7=88=20=EC=9E=AC=EC=A0=95=EC=9D=98=20(#38)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 📝 관련 이슈 및 PR **PR과 관련된 이슈 번호** - #37 ## ✨ 작업 내용 - 가격, 시간 등 테마를 정의하는데 필요하다고 느껴지는 필드 추가 - JPA Auditing으로 감사 정보 확인 기능 추가 - 프론트엔드 페이지 디자인 변경 및 새로운 API 반영 ## 🧪 테스트 6db81feb9b 을 바탕으로 향후 다른 모든 기능의 테스트를 통합 테스트로 전환할 예정. 지금은 불필요한 테스트가 너무 많다고 느껴짐. ## 📚 참고 자료 및 기타 - FInder / Writer / Validator 구조를 수정할 필요가 있음. 복잡하고 가독성이 낮은 로직만 별도로 빼는 것이 더 효율적이라고 판단됨. Reviewed-on: https://gitea.pricelees.me/pricelees/roomescape-refactored/pulls/38 Co-authored-by: pricelees Co-committed-by: pricelees --- frontend/src/App.tsx | 18 + frontend/src/api/apiClient.ts | 14 +- frontend/src/api/theme/themeAPI.ts | 36 +- frontend/src/api/theme/themeTypes.ts | 110 +++ frontend/src/components/Navbar.tsx | 57 +- frontend/src/css/admin-page.css | 17 + frontend/src/css/admin-reservation-page.css | 160 ++++ frontend/src/css/admin-theme-edit-page.css | 236 ++++++ frontend/src/css/admin-theme-page.css | 121 ++++ frontend/src/css/admin-time-page.css | 120 +++ frontend/src/css/admin-waiting-page.css | 81 +++ frontend/src/css/home-page-v2.css | 66 ++ frontend/src/css/login-page-v2.css | 74 ++ frontend/src/css/navbar.css | 117 +++ frontend/src/css/reservation-v2-1.css | 349 +++++++++ frontend/src/css/signup-page-v2.css | 65 ++ frontend/src/pages/LoginPage.tsx | 4 +- frontend/src/pages/admin/AdminNavbar.tsx | 59 +- frontend/src/pages/admin/AdminPage.tsx | 7 +- .../src/pages/admin/AdminThemeEditPage.tsx | 266 +++++++ frontend/src/pages/admin/ReservationPage.tsx | 142 ++-- frontend/src/pages/admin/ThemePage.tsx | 123 ++-- frontend/src/pages/admin/TimePage.tsx | 74 +- frontend/src/pages/admin/WaitingPage.tsx | 64 +- frontend/src/pages/v2/HomePageV2.tsx | 39 + frontend/src/pages/v2/LoginPageV2.tsx | 63 ++ .../src/pages/v2/ReservationStep1PageV21.tsx | 247 +++++++ .../src/pages/v2/ReservationStep2PageV21.tsx | 132 ++++ .../pages/v2/ReservationSuccessPageV21.tsx | 48 ++ frontend/src/pages/v2/SignupPageV2.tsx | 70 ++ frontend/src/util/DateTimeFormatter.ts | 35 + .../roomescape/RoomescapeApplication.kt | 2 - .../auth/web/support/AuthInterceptor.kt | 6 +- .../roomescape/common/config/JpaConfig.kt | 29 + .../roomescape/common/entity/BaseEntityV2.kt | 55 ++ .../common/log/ApiLogMessageConverter.kt | 5 +- .../theme/business/ThemeServiceV2.kt | 117 +++ .../theme/business/ThemeValidatorV2.kt | 114 +++ .../roomescape/theme/docs/ThemeApiV2.kt | 56 ++ .../theme/exception/ThemeErrorCode.kt | 6 + .../persistence/v2/ThemeEntityV2.kt | 66 ++ .../persistence/v2/ThemeRepositoryV2.kt | 12 + .../roomescape/theme/web/ThemeControllerV2.kt | 60 ++ .../kotlin/roomescape/theme/web/ThemeDtoV2.kt | 151 ++++ src/main/resources/login.http | 55 ++ src/main/resources/schema/schema-h2.sql | 22 + src/main/resources/schema/schema-mysql.sql | 22 + .../kotlin/roomescape/theme/ThemeApiTest.kt | 681 ++++++++++++++++++ .../kotlin/roomescape/util/KotestConfig.kt | 64 ++ .../roomescape/util/RestAssuredUtils.kt | 106 +++ 50 files changed, 4342 insertions(+), 301 deletions(-) create mode 100644 frontend/src/css/admin-page.css create mode 100644 frontend/src/css/admin-reservation-page.css create mode 100644 frontend/src/css/admin-theme-edit-page.css create mode 100644 frontend/src/css/admin-theme-page.css create mode 100644 frontend/src/css/admin-time-page.css create mode 100644 frontend/src/css/admin-waiting-page.css create mode 100644 frontend/src/css/home-page-v2.css create mode 100644 frontend/src/css/login-page-v2.css create mode 100644 frontend/src/css/navbar.css create mode 100644 frontend/src/css/reservation-v2-1.css create mode 100644 frontend/src/css/signup-page-v2.css create mode 100644 frontend/src/pages/admin/AdminThemeEditPage.tsx create mode 100644 frontend/src/pages/v2/HomePageV2.tsx create mode 100644 frontend/src/pages/v2/LoginPageV2.tsx create mode 100644 frontend/src/pages/v2/ReservationStep1PageV21.tsx create mode 100644 frontend/src/pages/v2/ReservationStep2PageV21.tsx create mode 100644 frontend/src/pages/v2/ReservationSuccessPageV21.tsx create mode 100644 frontend/src/pages/v2/SignupPageV2.tsx create mode 100644 frontend/src/util/DateTimeFormatter.ts create mode 100644 src/main/kotlin/roomescape/common/config/JpaConfig.kt create mode 100644 src/main/kotlin/roomescape/common/entity/BaseEntityV2.kt create mode 100644 src/main/kotlin/roomescape/theme/business/ThemeServiceV2.kt create mode 100644 src/main/kotlin/roomescape/theme/business/ThemeValidatorV2.kt create mode 100644 src/main/kotlin/roomescape/theme/docs/ThemeApiV2.kt create mode 100644 src/main/kotlin/roomescape/theme/infrastructure/persistence/v2/ThemeEntityV2.kt create mode 100644 src/main/kotlin/roomescape/theme/infrastructure/persistence/v2/ThemeRepositoryV2.kt create mode 100644 src/main/kotlin/roomescape/theme/web/ThemeControllerV2.kt create mode 100644 src/main/kotlin/roomescape/theme/web/ThemeDtoV2.kt create mode 100644 src/main/resources/login.http create mode 100644 src/test/kotlin/roomescape/theme/ThemeApiTest.kt create mode 100644 src/test/kotlin/roomescape/util/RestAssuredUtils.kt diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index d31faf7f..55f77c3e 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -17,6 +17,13 @@ import ReservationStep1Page from './pages/v2/ReservationStep1Page'; import ReservationStep2Page from './pages/v2/ReservationStep2Page'; import ReservationSuccessPage from './pages/v2/ReservationSuccessPage'; import MyReservationPageV2 from './pages/v2/MyReservationPageV2'; +import ReservationStep1PageV21 from './pages/v2/ReservationStep1PageV21'; +import ReservationStep2PageV21 from './pages/v2/ReservationStep2PageV21'; +import ReservationSuccessPageV21 from './pages/v2/ReservationSuccessPageV21'; +import HomePageV2 from './pages/v2/HomePageV2'; +import LoginPageV2 from './pages/v2/LoginPageV2'; +import SignupPageV2 from './pages/v2/SignupPageV2'; +import AdminThemeEditPage from './pages/admin/AdminThemeEditPage'; const AdminRoutes = () => ( @@ -25,6 +32,7 @@ const AdminRoutes = () => ( } /> } /> } /> + } /> } /> @@ -50,10 +58,20 @@ function App() { } /> } /> + {/* V2 Pages */} + } /> + } /> + } /> + {/* V2 Reservation Flow */} } /> } /> } /> + + {/* V2.1 Reservation Flow */} + } /> + } /> + } /> } /> diff --git a/frontend/src/api/apiClient.ts b/frontend/src/api/apiClient.ts index 6e488ac3..f72d764c 100644 --- a/frontend/src/api/apiClient.ts +++ b/frontend/src/api/apiClient.ts @@ -28,16 +28,16 @@ async function request( }, }; - if (isRequiredAuth) { - const accessToken = localStorage.getItem('accessToken'); - if (accessToken) { - if (!config.headers) { - config.headers = {}; - } - config.headers['Authorization'] = `Bearer ${accessToken}`; + + const accessToken = localStorage.getItem('accessToken'); + if (accessToken) { + if (!config.headers) { + config.headers = {}; } + config.headers['Authorization'] = `Bearer ${accessToken}`; } + if (method.toUpperCase() !== 'GET') { config.data = data; } diff --git a/frontend/src/api/theme/themeAPI.ts b/frontend/src/api/theme/themeAPI.ts index 6cbe5c3d..b36ca302 100644 --- a/frontend/src/api/theme/themeAPI.ts +++ b/frontend/src/api/theme/themeAPI.ts @@ -1,5 +1,13 @@ -import apiClient from "@_api/apiClient"; -import type { ThemeCreateRequest, ThemeCreateResponse, ThemeRetrieveListResponse } from "./themeTypes"; +import apiClient from '@_api/apiClient'; +import type { + AdminThemeDetailRetrieveResponse, + AdminThemeSummaryRetrieveListResponse, + ThemeCreateRequest, + ThemeCreateRequestV2, ThemeCreateResponse, + ThemeCreateResponseV2, ThemeRetrieveListResponse, + ThemeUpdateRequest, + UserThemeRetrieveListResponse +} from './themeTypes'; export const createTheme = async (data: ThemeCreateRequest): Promise => { return await apiClient.post('/themes', data, true); @@ -16,3 +24,27 @@ export const mostReservedThemes = async (count: number = 10): Promise => { return await apiClient.del(`/themes/${id}`, true); }; + +export const fetchAdminThemes = async (): Promise => { + return await apiClient.get('/admin/themes'); +}; + +export const fetchAdminThemeDetail = async (id: string): Promise => { + return await apiClient.get(`/admin/themes/${id}`); +}; + +export const createThemeV2 = async (themeData: ThemeCreateRequestV2): Promise => { + return await apiClient.post('/admin/themes', themeData); +}; + +export const updateTheme = async (id: string, themeData: ThemeUpdateRequest): Promise => { + await apiClient.patch(`/admin/themes/${id}`, themeData); +}; + +export const deleteTheme = async (id: string): Promise => { + await apiClient.del(`/admin/themes/${id}`); +}; + +export const fetchUserThemes = async (): Promise => { + return await apiClient.get('/v2/themes'); +}; diff --git a/frontend/src/api/theme/themeTypes.ts b/frontend/src/api/theme/themeTypes.ts index 129f1fc1..dd24ed8e 100644 --- a/frontend/src/api/theme/themeTypes.ts +++ b/frontend/src/api/theme/themeTypes.ts @@ -21,3 +21,113 @@ export interface ThemeRetrieveResponse { export interface ThemeRetrieveListResponse { themes: ThemeRetrieveResponse[]; } + + +export interface ThemeV2 { + id: string; + name: string; + description: string; + thumbnailUrl: string; + difficulty: Difficulty; + price: number; + minParticipants: number; + maxParticipants: number; + availableMinutes: number; + expectedMinutesFrom: number; + expectedMinutesTo: number; + isOpen: boolean; + createDate: string; // Assuming ISO string format + updatedDate: string; // Assuming ISO string format + createdBy: string; + updatedBy: string; +} + +export interface ThemeCreateRequestV2 { + name: string; + description: string; + thumbnailUrl: string; + difficulty: Difficulty; + price: number; + minParticipants: number; + maxParticipants: number; + availableMinutes: number; + expectedMinutesFrom: number; + expectedMinutesTo: number; + isOpen: boolean; +} + +export interface ThemeCreateResponseV2 { + id: string; +} + +export interface ThemeUpdateRequest { + name?: string; + description?: string; + thumbnailUrl?: string; + difficulty?: Difficulty; + price?: number; + minParticipants?: number; + maxParticipants?: number; + availableMinutes?: number; + expectedMinutesFrom?: number; + expectedMinutesTo?: number; + isOpen?: boolean; +} + +export interface AdminThemeSummaryRetrieveResponse { + id: string; + name: string; + difficulty: Difficulty; + price: number; + isOpen: boolean; +} + +export interface AdminThemeSummaryRetrieveListResponse { + themes: AdminThemeSummaryRetrieveResponse[]; +} + +export interface AdminThemeDetailRetrieveResponse { + id: string; + name: string; + description: string; + thumbnailUrl: string; + difficulty: Difficulty; + price: number; + minParticipants: number; + maxParticipants: number; + availableMinutes: number; + expectedMinutesFrom: number; + expectedMinutesTo: number; + isOpen: boolean; + createdAt: string; // LocalDateTime in Kotlin, map to string (ISO format) + createdBy: string; + updatedAt: string; // LocalDateTime in Kotlin, map to string (ISO format) + updatedBy: string; +} + +export interface UserThemeRetrieveResponse { + id: string; + name: string; + thumbnailUrl: string; + description: string; + difficulty: Difficulty; + price: number; + minParticipants: number; + maxParticipants: number; + availableMinutes: number; + expectedMinutesFrom: number; + expectedMinutesTo: number; +} + +export interface UserThemeRetrieveListResponse { + themes: UserThemeRetrieveResponse[]; +} + +// @ts-ignore +export enum Difficulty { + VERY_EASY = 'VERY_EASY', + EASY = 'EASY', + NORMAL = 'NORMAL', + HARD = 'HARD', + VERY_HARD = 'VERY_HARD', +} \ No newline at end of file diff --git a/frontend/src/components/Navbar.tsx b/frontend/src/components/Navbar.tsx index 8644022f..82168dba 100644 --- a/frontend/src/components/Navbar.tsx +++ b/frontend/src/components/Navbar.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { Link, useNavigate } from 'react-router-dom'; import { useAuth } from 'src/context/AuthContext'; +import 'src/css/navbar.css'; const Navbar: React.FC = () => { const { loggedIn, userName, logout } = useAuth(); @@ -14,42 +15,34 @@ const Navbar: React.FC = () => { } catch (error) { console.error('Logout failed:', error); } - } + }; return ( -