From 9e7eb7da2b109218c0448283ff3e35226d1b5b7e Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 20 Jun 2025 12:27:55 +0900 Subject: [PATCH] login store menu function edit --- src/components/layout/MainLayout.vue | 877 ++++++-------------- src/router/index.js | 97 ++- src/services/store.js | 121 +-- src/store/index.js | 159 ++-- src/views/LoginView.vue | 991 +++++++++++++++------- src/views/StoreManagementView.vue | 1148 ++++++++++++++------------ 6 files changed, 1831 insertions(+), 1562 deletions(-) diff --git a/src/components/layout/MainLayout.vue b/src/components/layout/MainLayout.vue index 9f21ad8..e815c92 100644 --- a/src/components/layout/MainLayout.vue +++ b/src/components/layout/MainLayout.vue @@ -1,658 +1,301 @@ -//* src/components/layout/MainLayout.vue - + + + \ No newline at end of file diff --git a/src/router/index.js b/src/router/index.js index 68b8980..918fc33 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -1,8 +1,5 @@ -//* src/router/index.js -/** - * Vue Router 설정 - * 라우팅 및 네비게이션 가드 설정 - */ +// src/router/index.js - 완전히 수정된 버전 + import { createRouter, createWebHistory } from 'vue-router' // 뷰 컴포넌트 lazy loading @@ -15,7 +12,7 @@ const ContentManagementView = () => import('@/views/ContentManagementView.vue') const routes = [ { path: '/', - redirect: '/login', // 항상 로그인 페이지로 먼저 리다이렉트 + redirect: '/login', }, { path: '/login', @@ -32,6 +29,7 @@ const routes = [ component: DashboardView, meta: { requiresAuth: true, + requiresStore: true, // ✅ 매장 정보 필수 title: '대시보드', }, }, @@ -41,6 +39,7 @@ const routes = [ component: StoreManagementView, meta: { requiresAuth: true, + requiresStore: false, // ✅ 매장 정보 없어도 접근 가능 title: '매장 관리', }, }, @@ -50,6 +49,7 @@ const routes = [ component: ContentCreationView, meta: { requiresAuth: true, + requiresStore: true, // ✅ 매장 정보 필수 title: '콘텐츠 생성', }, }, @@ -59,12 +59,13 @@ const routes = [ component: ContentManagementView, meta: { requiresAuth: true, + requiresStore: true, // ✅ 매장 정보 필수 title: '콘텐츠 관리', }, }, { path: '/:pathMatch(.*)*', - redirect: '/login', // 404시 로그인으로 이동 + redirect: '/login', }, ] @@ -73,12 +74,12 @@ const router = createRouter({ routes, }) -// 네비게이션 가드 - 수정된 버전 +// ✅ 개선된 네비게이션 가드 router.beforeEach(async (to, from, next) => { console.log('=== 라우터 가드 실행 ===') console.log('이동 경로:', `${from.path} → ${to.path}`) - // Pinia 스토어를 동적으로 가져오기 (순환 참조 방지) + // Pinia 스토어를 동적으로 가져오기 const { useAuthStore } = await import('@/store/auth') const authStore = useAuthStore() @@ -89,19 +90,79 @@ router.beforeEach(async (to, from, next) => { console.log('토큰 존재:', !!authStore.token) console.log('사용자 정보:', authStore.user?.nickname) - // 인증이 필요한 페이지인지 확인 + // 1단계: 인증 체크 const requiresAuth = to.meta.requiresAuth !== false if (requiresAuth && !authStore.isAuthenticated) { - console.log('인증 필요 - 로그인 페이지로 이동') + console.log('🚫 인증 필요 - 로그인 페이지로 이동') next('/login') - } else if (to.path === '/login' && authStore.isAuthenticated) { - console.log('이미 로그인됨 - 대시보드로 이동') - next('/dashboard') - } else { - console.log('이동 허용:', to.path) - next() + return } + + if (to.path === '/login' && authStore.isAuthenticated) { + console.log('✅ 이미 로그인됨 - 매장 정보 체크 후 리다이렉트') + + // 로그인 상태에서 /login 접근 시 매장 정보에 따라 리다이렉트 + try { + const { useStoreStore } = await import('@/store/index') + const storeStore = useStoreStore() + const result = await storeStore.fetchStoreInfo() + + if (result.success && result.data) { + console.log('🏪 매장 정보 있음 - 대시보드로 이동') + next('/dashboard') + } else { + console.log('📝 매장 정보 없음 - 매장 관리로 이동') + next('/store') + } + } catch (error) { + console.log('❌ 매장 정보 조회 실패 - 매장 관리로 이동') + next('/store') + } + return + } + + // 2단계: 매장 정보 체크 (인증된 사용자만) + const requiresStore = to.meta.requiresStore === true + + if (authStore.isAuthenticated && requiresStore) { + console.log('🏪 매장 정보 체크 필요한 페이지:', to.name) + + try { + const { useStoreStore } = await import('@/store/index') + const storeStore = useStoreStore() + + // 매장 정보 조회 + const result = await storeStore.fetchStoreInfo() + + if (!result.success || !result.data) { + console.log('🚫 매장 정보 없음 - 매장 관리 페이지로 리다이렉트') + + // 사용자에게 알림 (스낵바) + const { useAppStore } = await import('@/store/app') + const appStore = useAppStore() + appStore.showSnackbar('매장 정보를 먼저 등록해주세요', 'warning') + + next('/store') + return + } else { + console.log('✅ 매장 정보 확인됨 - 페이지 접근 허용') + } + } catch (error) { + console.log('❌ 매장 정보 조회 실패 - 매장 관리 페이지로 리다이렉트') + + // 에러 시에도 매장 관리로 + const { useAppStore } = await import('@/store/app') + const appStore = useAppStore() + appStore.showSnackbar('매장 정보를 확인할 수 없습니다. 매장 정보를 등록해주세요', 'error') + + next('/store') + return + } + } + + console.log('✅ 이동 허용:', to.path) + next() }) router.afterEach((to) => { @@ -109,4 +170,4 @@ router.afterEach((to) => { document.title = to.meta.title ? `${to.meta.title} - AI 마케팅` : 'AI 마케팅' }) -export default router +export default router \ No newline at end of file diff --git a/src/services/store.js b/src/services/store.js index 94abee9..429c544 100644 --- a/src/services/store.js +++ b/src/services/store.js @@ -69,59 +69,78 @@ class StoreService { * @returns {Promise} 매장 정보 */ async getStore() { - try { - console.log('=== 매장 정보 조회 API 호출 ===') - - // URL 슬래시 문제 해결: 빈 문자열로 호출하여 '/api/store'가 되도록 함 - const response = await storeApi.get('') - - console.log('매장 정보 조회 API 응답:', response.data) - - // 백엔드 응답 구조 수정: 디버깅 결과에 맞게 처리 - if (response.data && response.data.status === 200 && response.data.data) { - console.log('✅ 매장 정보 조회 성공:', response.data.data) - return { - success: true, - message: response.data.message || '매장 정보를 조회했습니다.', - data: response.data.data - } - } else if (response.data && response.data.status === 404) { - // 매장이 없는 경우 - console.log('⚠️ 등록된 매장이 없음') - return { - success: false, - message: '등록된 매장이 없습니다', - data: null - } - } else { - console.warn('예상치 못한 응답 구조:', response.data) - throw new Error(response.data.message || '매장 정보를 찾을 수 없습니다.') + try { + console.log('=== 매장 정보 조회 API 호출 ===') + + const response = await storeApi.get('') + + console.log('매장 정보 조회 API 응답:', response.data) + + // 성공 응답 처리 + if (response.data && response.data.status === 200 && response.data.data) { + console.log('✅ 매장 정보 조회 성공:', response.data.data) + return { + success: true, + message: response.data.message || '매장 정보를 조회했습니다.', + data: response.data.data } - } catch (error) { - console.error('매장 정보 조회 실패:', error) - - // 404 오류 처리 (매장이 없음) - if (error.response?.status === 404) { - return { - success: false, - message: '등록된 매장이 없습니다', - data: null - } + } else if (response.data && response.data.status === 404) { + // 매장이 없는 경우 + console.log('📝 등록된 매장이 없음 (정상)') + return { + success: false, + message: '등록된 매장이 없습니다', + data: null } - - // 500 오류 처리 (서버 내부 오류) - if (error.response?.status === 500) { - console.error('서버 내부 오류 - 백엔드 로그 확인 필요:', error.response?.data) - return { - success: false, - message: '서버 오류가 발생했습니다. 관리자에게 문의하세요.', - data: null - } + } else { + console.log('예상치 못한 응답 구조:', response.data) + return { + success: false, + message: '등록된 매장이 없습니다', + data: null } - - return handleApiError(error) + } + } catch (error) { + console.log('매장 정보 조회 중 오류:', error.message) + + // 404 오류 - 매장이 없음 (정상) + if (error.response?.status === 404) { + console.log('📝 404: 등록된 매장이 없음 (정상)') + return { + success: false, + message: '등록된 매장이 없습니다', + data: null + } + } + + // 500 오류 - 서버 에러지만 매장이 없어서 발생할 수 있음 + if (error.response?.status === 500) { + console.log('📝 500: 서버 에러 - 매장 없음으로 간주') + return { + success: false, + message: '등록된 매장이 없습니다', + data: null + } + } + + // 401 오류 - 인증 문제 + if (error.response?.status === 401) { + return { + success: false, + message: '로그인이 필요합니다', + data: null + } + } + + // 기타 모든 에러도 매장 없음으로 간주 + console.log('📝 기타 에러 - 매장 없음으로 간주') + return { + success: false, + message: '등록된 매장이 없습니다', + data: null } } +} /** * 매장 정보 수정 (STR-010: 매장 수정) @@ -140,10 +159,8 @@ class StoreService { businessType: storeData.businessType, address: storeData.address, phoneNumber: storeData.phoneNumber, - // ✅ 수정: businessHours 필드 처리 - businessHours: storeData.businessHours || `${storeData.openTime || '09:00'}-${storeData.closeTime || '21:00'}`, - // ✅ 수정: closedDays 필드 처리 - closedDays: storeData.closedDays || storeData.holidays || '', + businessHours: storeData.businessHours, // 그대로 전달 + closedDays: storeData.closedDays, // 그대로 전달 seatCount: parseInt(storeData.seatCount) || 0, instaAccounts: storeData.instaAccounts || '', blogAccounts: storeData.blogAccounts || '', diff --git a/src/store/index.js b/src/store/index.js index 98da823..b5cb2a1 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -12,7 +12,25 @@ export const useStoreStore = defineStore('store', { getters: { hasStoreInfo: (state) => !!state.storeInfo, isLoading: (state) => state.loading, - hasMenus: (state) => state.menus && state.menus.length > 0 + hasMenus: (state) => state.menus && state.menus.length > 0, + + storeInfoSummary: (state) => { + if (!state.storeInfo) { + return { + hasStore: false, + message: '매장 정보를 등록해주세요', + action: '등록하기' + } + } + + return { + hasStore: true, + storeName: state.storeInfo.storeName, + businessType: state.storeInfo.businessType, + message: `${state.storeInfo.storeName} 운영 중`, + action: '관리하기' + } + } }, actions: { @@ -20,62 +38,107 @@ export const useStoreStore = defineStore('store', { * 매장 정보 조회 */ async fetchStoreInfo() { - console.log('=== Store 스토어: 매장 정보 조회 시작 ===') - this.loading = true - this.error = null + console.log('=== Store 스토어: 매장 정보 조회 시작 ===') + this.loading = true + this.error = null + + try { + // 스토어 서비스 임포트 + const { storeService } = await import('@/services/store') + + console.log('매장 정보 API 호출') + const result = await storeService.getStore() + + console.log('=== Store 스토어: API 응답 분석 ===') + console.log('Result:', result) + console.log('Result.success:', result.success) + console.log('Result.data:', result.data) + console.log('Result.message:', result.message) + + if (result.success && result.data) { + // 매장 정보가 있는 경우 + console.log('✅ 매장 정보 설정:', result.data) + this.storeInfo = result.data + return { success: true, data: result.data } + } else { + // 매장이 없거나 조회 실패한 경우 + console.log('📝 매장 정보 없음 - 신규 사용자') + this.storeInfo = null + // 매장이 없는 것은 정상 상황이므로 success: false이지만 에러가 아님 + return { + success: false, + message: '등록된 매장이 없습니다', + isNewUser: true // 신규 사용자 플래그 추가 + } + } + } catch (error) { + console.log('=== Store 스토어: 매장 정보 조회 중 오류 ===') + console.log('Error:', error.message) + + this.error = null // 에러 상태를 설정하지 않음 + this.storeInfo = null + + // HTTP 상태 코드별 처리 - 모두 신규 사용자로 간주 + if (error.response?.status === 404) { + return { + success: false, + message: '등록된 매장이 없습니다', + isNewUser: true + } + } + + if (error.response?.status >= 500) { + // 서버 에러도 신규 사용자로 간주 (매장이 없어서 발생할 수 있음) + console.log('서버 에러 발생, 신규 사용자로 간주') + return { + success: false, + message: '등록된 매장이 없습니다', + isNewUser: true + } + } + + if (error.response?.status === 401) { + return { + success: false, + message: '로그인이 필요합니다', + needLogin: true + } + } + + // 기타 모든 에러도 신규 사용자로 간주 + return { + success: false, + message: '등록된 매장이 없습니다', + isNewUser: true + } + } finally { + this.loading = false + } +}, +async getLoginRedirectPath() { try { - // 스토어 서비스 임포트 - const { storeService } = await import('@/services/store') - - console.log('매장 정보 API 호출') - const result = await storeService.getStore() - - console.log('=== Store 스토어: API 응답 분석 ===') - console.log('Result:', result) - console.log('Result.success:', result.success) - console.log('Result.data:', result.data) - console.log('Result.message:', result.message) + const result = await this.fetchStoreInfo() if (result.success && result.data) { - // 매장 정보가 있는 경우 - console.log('✅ 매장 정보 설정:', result.data) - this.storeInfo = result.data - return { success: true, data: result.data } + return { + path: '/dashboard', + message: `${result.data.storeName}에 오신 것을 환영합니다!`, + type: 'success' + } } else { - // 매장이 없거나 조회 실패한 경우 - console.log('⚠️ 매장 정보 없음 또는 조회 실패') - this.storeInfo = null - - if (result.message === '등록된 매장이 없습니다') { - return { success: false, message: '등록된 매장이 없습니다' } - } else { - return { success: false, message: result.message || '매장 정보 조회에 실패했습니다' } + return { + path: '/store', + message: '매장 정보를 등록하고 AI 마케팅을 시작해보세요!', + type: 'info' } } } catch (error) { - console.error('=== Store 스토어: 매장 정보 조회 실패 ===') - console.error('Error:', error) - - this.error = error.message - this.storeInfo = null - - // HTTP 상태 코드별 처리 - if (error.response?.status === 404) { - return { success: false, message: '등록된 매장이 없습니다' } + return { + path: '/store', + message: '매장 정보를 확인할 수 없습니다. 매장 정보를 등록해주세요', + type: 'warning' } - - if (error.response?.status >= 500) { - return { success: false, message: '서버 오류가 발생했습니다. 잠시 후 다시 시도해주세요.' } - } - - if (error.response?.status === 401) { - return { success: false, message: '로그인이 필요합니다' } - } - - return { success: false, message: error.message || '매장 정보 조회에 실패했습니다' } - } finally { - this.loading = false } }, diff --git a/src/views/LoginView.vue b/src/views/LoginView.vue index ef9a0ee..8fabc1b 100644 --- a/src/views/LoginView.vue +++ b/src/views/LoginView.vue @@ -1,62 +1,96 @@ -//* src/views/LoginView.vue - 수정된 회원가입 기능 +//* src/views/LoginView.vue - 모던하고 세련된 디자인으로 개선