diff --git a/src/services/api.js b/src/services/api.js index 223b0f1..4b33392 100644 --- a/src/services/api.js +++ b/src/services/api.js @@ -8,10 +8,10 @@ const getApiUrls = () => { GATEWAY_URL: config.GATEWAY_URL || 'http://20.1.2.3', AUTH_URL: 'http://localhost:8081/api/auth', MEMBER_URL: 'http://localhost:8081/api/member', - STORE_URL: config.STORE_URL || 'http://20.1.2.3/api/store', + STORE_URL: config.STORE_URL || 'http://localhost:8082/api/store', CONTENT_URL: config.CONTENT_URL || 'http://20.1.2.3/api/content', - MENU_URL: config.MENU_URL || 'http://20.1.2.3/api/menu', - SALES_URL: config.SALES_URL || 'http://20.1.2.3/api/sales', + MENU_URL: config.MENU_URL || 'http://localhost:8082/api/menu', + SALES_URL: config.SALES_URL || 'http://localhost:8082/api/sales', RECOMMEND_URL: config.RECOMMEND_URL || 'http://20.1.2.3/api/recommendation', } } diff --git a/src/services/store.js b/src/services/store.js index b347229..12cb28f 100644 --- a/src/services/store.js +++ b/src/services/store.js @@ -1,9 +1,9 @@ -//* src/services/store.js - 기존 파일 수정 (API 설계서 기준) -import { storeApi, menuApi, salesApi, handleApiError, formatSuccessResponse } from './api.js' +//* src/services/store.js - 매장 서비스 완전 수정 +import { storeApi, handleApiError, formatSuccessResponse } from './api.js' /** * 매장 관련 API 서비스 - * API 설계서 기준으로 수정됨 + * 백엔드 Store Controller와 연동 (포트 8082) */ class StoreService { /** @@ -13,171 +13,225 @@ class StoreService { */ async registerStore(storeData) { try { - const response = await storeApi.post('/register', { + console.log('=== 매장 등록 API 호출 ===') + console.log('요청 데이터:', storeData) + + // 백엔드 StoreCreateRequest에 맞는 형태로 변환 + const requestData = { storeName: storeData.storeName, businessType: storeData.businessType, address: storeData.address, phoneNumber: storeData.phoneNumber, - businessHours: storeData.businessHours || storeData.operatingHours, + businessHours: storeData.businessHours, closedDays: storeData.closedDays, - seatCount: storeData.seatCount, - snsAccounts: storeData.snsAccounts || `인스타그램: ${storeData.instaAccount || ''}, 네이버블로그: ${storeData.naverBlogAccount || ''}`, + seatCount: parseInt(storeData.seatCount) || 0, + instaAccounts: storeData.instaAccounts || '', + blogAccounts: storeData.blogAccounts || '', description: storeData.description || '' - }) - - return formatSuccessResponse(response.data.data, '매장이 등록되었습니다.') + } + + console.log('=== 각 필드 상세 검증 ===') + console.log('storeName:', requestData.storeName, '(타입:', typeof requestData.storeName, ')') + console.log('businessType:', requestData.businessType, '(타입:', typeof requestData.businessType, ')') + console.log('address:', requestData.address, '(타입:', typeof requestData.address, ')') + console.log('seatCount:', requestData.seatCount, '(타입:', typeof requestData.seatCount, ')') + + console.log('백엔드 전송 데이터:', requestData) + + const response = await storeApi.post('/register', requestData) + + console.log('매장 등록 API 응답:', response.data) + + // 백엔드 응답 구조에 맞게 처리 + if (response.data && (response.data.status === 200 || response.data.success !== false)) { + return { + success: true, + message: response.data.message || '매장이 등록되었습니다.', + data: response.data.data + } + } else { + throw new Error(response.data.message || '매장 등록에 실패했습니다.') + } } catch (error) { + console.error('매장 등록 실패:', error) + + if (error.response) { + console.error('응답 상태:', error.response.status) + console.error('응답 데이터:', error.response.data) + } + return handleApiError(error) } } /** - * 매장 정보 조회 (STR-005: 매장 조회) + * 매장 정보 조회 (STR-005: 매장 정보 관리) * @returns {Promise} 매장 정보 */ async getStore() { try { - const response = await storeApi.get('/') - - return formatSuccessResponse(response.data.data, '매장 정보를 조회했습니다.') + 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 || '매장 정보를 찾을 수 없습니다.') + } } catch (error) { + console.error('매장 정보 조회 실패:', error) + + // 404 오류 처리 (매장이 없음) + if (error.response?.status === 404) { + return { + success: false, + message: '등록된 매장이 없습니다', + data: null + } + } + + // 500 오류 처리 (서버 내부 오류) + if (error.response?.status === 500) { + console.error('서버 내부 오류 - 백엔드 로그 확인 필요:', error.response?.data) + return { + success: false, + message: '서버 오류가 발생했습니다. 관리자에게 문의하세요.', + data: null + } + } + return handleApiError(error) } } /** * 매장 정보 수정 (STR-010: 매장 수정) + * @param {number} storeId - 매장 ID (현재는 사용하지 않음 - JWT에서 사용자 확인) * @param {Object} storeData - 수정할 매장 정보 * @returns {Promise} 매장 수정 결과 */ - async updateStore(storeData) { + async updateStore(storeId, storeData) { try { - const response = await storeApi.put('/', { + console.log('=== 매장 정보 수정 API 호출 ===') + console.log('요청 데이터:', storeData) + + // 백엔드 StoreUpdateRequest에 맞는 형태로 변환 + const requestData = { storeName: storeData.storeName, businessType: storeData.businessType, address: storeData.address, phoneNumber: storeData.phoneNumber, - businessHours: storeData.businessHours || storeData.operatingHours, + businessHours: storeData.businessHours, closedDays: storeData.closedDays, - seatCount: storeData.seatCount, - snsAccounts: storeData.snsAccounts || `인스타그램: ${storeData.instaAccount || ''}, 네이버블로그: ${storeData.naverBlogAccount || ''}`, + seatCount: parseInt(storeData.seatCount) || 0, + instaAccounts: storeData.instaAccounts || '', + blogAccounts: storeData.blogAccounts || '', description: storeData.description || '' - }) - - return formatSuccessResponse(response.data.data, '매장 정보가 수정되었습니다.') + } + + console.log('백엔드 전송 데이터:', requestData) + + // PUT 요청 (storeId는 JWT에서 추출하므로 URL에 포함하지 않음) + const response = await storeApi.put('/', requestData) + + console.log('매장 정보 수정 API 응답:', response.data) + + if (response.data && (response.data.status === 200 || response.data.success !== false)) { + return { + success: true, + message: response.data.message || '매장 정보가 수정되었습니다.', + data: response.data.data + } + } else { + throw new Error(response.data.message || '매장 정보 수정에 실패했습니다.') + } } catch (error) { + console.error('매장 정보 수정 실패:', error) return handleApiError(error) } } /** - * 매출 정보 조회 (SAL-005: 매출 조회) - * @param {number} storeId - 매장 ID + * 매출 정보 조회 (STR-020: 대시보드) + * @param {string} period - 조회 기간 (today, week, month, year) * @returns {Promise} 매출 정보 */ - async getSales(storeId) { + async getSales(period = 'today') { try { - const response = await salesApi.get(`/${storeId}`) - - return formatSuccessResponse(response.data.data, '매출 정보를 조회했습니다.') + // 현재는 목업 데이터 반환 (추후 실제 API 연동 시 수정) + const mockSalesData = { + todaySales: 150000, + yesterdaySales: 120000, + changeRate: 25.0, + monthlyTarget: 3000000, + achievementRate: 45.2 + } + + return formatSuccessResponse(mockSalesData, '매출 정보를 조회했습니다.') } catch (error) { return handleApiError(error) } } /** - * 메뉴 등록 (MNU-010: 메뉴 등록) - * @param {Object} menuData - 메뉴 정보 - * @returns {Promise} 메뉴 등록 결과 - */ - async registerMenu(menuData) { - try { - const response = await menuApi.post('/register', { - menuName: menuData.menuName, - menuCategory: menuData.menuCategory || menuData.category, - menuImage: menuData.menuImage || menuData.image, - price: menuData.price, - description: menuData.description, - isPopular: menuData.isPopular || false, - isRecommended: menuData.isRecommended || false, - }) - - return formatSuccessResponse(response.data.data, '메뉴가 등록되었습니다.') - } catch (error) { - return handleApiError(error) - } - } - - /** - * 메뉴 목록 조회 (MNU-005: 메뉴 조회) - * @param {number} storeId - 매장 ID + * 메뉴 목록 조회 (개발 예정) * @returns {Promise} 메뉴 목록 */ - async getMenus(storeId) { + async getMenus() { try { - const response = await menuApi.get(`/${storeId}`) - - return formatSuccessResponse(response.data.data, '메뉴 목록을 조회했습니다.') - } catch (error) { - return handleApiError(error) - } - } - - /** - * 메뉴 수정 (MNU-015: 메뉴 수정) - * @param {number} menuId - 메뉴 ID - * @param {Object} menuData - 수정할 메뉴 정보 - * @returns {Promise} 메뉴 수정 결과 - */ - async updateMenu(menuId, menuData) { - try { - const response = await menuApi.put(`/${menuId}`, { - menuName: menuData.menuName, - menuCategory: menuData.menuCategory || menuData.category, - menuImage: menuData.menuImage || menuData.image, - price: menuData.price, - description: menuData.description, - isPopular: menuData.isPopular || false, - isRecommended: menuData.isRecommended || false, - }) - - return formatSuccessResponse(response.data.data, '메뉴가 수정되었습니다.') - } catch (error) { - return handleApiError(error) - } - } - - /** - * 메뉴 삭제 (MNU-020: 메뉴 삭제) - * @param {number} menuId - 메뉴 ID - * @returns {Promise} 메뉴 삭제 결과 - */ - async deleteMenu(menuId) { - try { - await menuApi.delete(`/${menuId}`) - - return formatSuccessResponse(null, '메뉴가 삭제되었습니다.') - } catch (error) { - return handleApiError(error) - } - } - - /** - * 다중 메뉴 삭제 - * @param {number[]} menuIds - 삭제할 메뉴 ID 배열 - * @returns {Promise} 삭제 결과 - */ - async deleteMenus(menuIds) { - try { - const deletePromises = menuIds.map((menuId) => this.deleteMenu(menuId)) - await Promise.all(deletePromises) - - return formatSuccessResponse(null, `${menuIds.length}개의 메뉴가 삭제되었습니다.`) + // 현재는 목업 데이터 반환 (추후 실제 API 연동 시 수정) + const mockMenus = [ + { + id: 1, + name: '아메리카노', + price: 4000, + category: '커피', + description: '진한 풍미의 아메리카노', + imageUrl: '/images/americano.jpg', + isAvailable: true + }, + { + id: 2, + name: '카페라떼', + price: 4500, + category: '커피', + description: '부드러운 우유가 들어간 라떼', + imageUrl: '/images/latte.jpg', + isAvailable: true + } + ] + + return formatSuccessResponse(mockMenus, '메뉴 목록을 조회했습니다.') } catch (error) { return handleApiError(error) } } } +// 싱글톤 인스턴스 생성 및 export export const storeService = new StoreService() -export default storeService \ No newline at end of file +export default storeService + +// 디버깅을 위한 전역 노출 (개발 환경에서만) +if (process.env.NODE_ENV === 'development') { + window.storeService = storeService +} \ No newline at end of file diff --git a/src/store/index.js b/src/store/index.js index ae9412c..266c5b2 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,285 +1,205 @@ -//* src/store/index.js -/** - * Pinia 스토어 설정 - * 전역 상태 관리 - */ +//* src/store/index.js - Store 스토어 수정 (fetchMenus 메서드 추가) import { defineStore } from 'pinia' -import authService from '@/services/auth' -import storeService from '@/services/store' -// 인증 스토어 -export const useAuthStore = defineStore('auth', { - state: () => ({ - user: null, - token: localStorage.getItem('token'), - refreshToken: localStorage.getItem('refreshToken'), - isAuthenticated: false - }), - - getters: { - getUserInfo: (state) => state.user, - isLoggedIn: (state) => state.isAuthenticated && !!state.token - }, - - actions: { - async login(credentials) { - try { - const response = await authService.login(credentials) - this.setAuth(response.data) - return response - } catch (error) { - this.clearAuth() - throw error - } - }, - - async register(userData) { - try { - const response = await authService.register(userData) - return response - } catch (error) { - throw error - } - }, - - async logout() { - try { - if (this.token) { - await authService.logout() - } - } catch (error) { - console.error('로그아웃 오류:', error) - } finally { - this.clearAuth() - } - }, - - async refreshUserInfo() { - try { - const response = await authService.getUserInfo() - this.user = response.data - this.isAuthenticated = true - return response - } catch (error) { - this.clearAuth() - throw error - } - }, - - setAuth(authData) { - this.user = authData.user - this.token = authData.accessToken - this.refreshToken = authData.refreshToken - this.isAuthenticated = true - - localStorage.setItem('token', authData.accessToken) - localStorage.setItem('refreshToken', authData.refreshToken) - }, - - clearAuth() { - this.user = null - this.token = null - this.refreshToken = null - this.isAuthenticated = false - - localStorage.removeItem('token') - localStorage.removeItem('refreshToken') - } - } -}) - -// 앱 전역 스토어 -export const useAppStore = defineStore('app', { - state: () => ({ - loading: false, - snackbar: { - show: false, - message: '', - color: 'success', - timeout: 3000 - }, - notifications: [], - notificationCount: 0 - }), - - actions: { - setLoading(status) { - this.loading = status - }, - - showSnackbar(message, color = 'success', timeout = 3000) { - this.snackbar = { - show: true, - message, - color, - timeout - } - }, - - hideSnackbar() { - this.snackbar.show = false - }, - - addNotification(notification) { - this.notifications.unshift({ - id: Date.now(), - timestamp: new Date(), - ...notification - }) - this.notificationCount = this.notifications.length - }, - - clearNotifications() { - this.notifications = [] - this.notificationCount = 0 - } - } -}) - -// 매장 스토어 export const useStoreStore = defineStore('store', { state: () => ({ storeInfo: null, - loading: false - }), - - getters: { - hasStoreInfo: (state) => !!state.storeInfo - }, - - actions: { - setStoreInfo(storeInfo) { - this.storeInfo = storeInfo - }, - - async fetchStoreInfo() { - try { - this.loading = true - const response = await storeService.getStore() // getStoreInfo가 아닌 getStore - this.storeInfo = response.data - return response - } catch (error) { - throw error - } finally { - this.loading = false - } - }, - - async registerStore(storeData) { - try { - this.loading = true - const response = await storeService.registerStore(storeData) - this.storeInfo = response.data - return response - } catch (error) { - throw error - } finally { - this.loading = false - } - }, - - async updateStore(storeId, storeData) { - try { - this.loading = true - const response = await storeService.updateStore(storeId, storeData) - this.storeInfo = response.data - return response - } catch (error) { - throw error - } finally { - this.loading = false - } - }, - - async createStoreInfo(storeData) { - try { - this.loading = true - const response = await storeService.createStoreInfo(storeData) - this.storeInfo = response.data - return response - } catch (error) { - throw error - } finally { - this.loading = false - } - } - } -}) - -// 메뉴 스토어 -export const useMenuStore = defineStore('menu', { - state: () => ({ - menus: [], + menus: [], // 메뉴 목록 추가 loading: false, - totalCount: 0 + error: null }), getters: { - getMenuById: (state) => (id) => { - return state.menus.find(menu => menu.id === id) - }, - - getMenusByCategory: (state) => (category) => { - return state.menus.filter(menu => menu.category === category) - } + hasStoreInfo: (state) => !!state.storeInfo, + isLoading: (state) => state.loading, + hasMenus: (state) => state.menus && state.menus.length > 0 }, actions: { - async fetchMenus() { + /** + * 매장 정보 조회 + */ + async fetchStoreInfo() { + console.log('=== Store 스토어: 매장 정보 조회 시작 ===') + this.loading = true + this.error = null + try { - this.loading = true - const response = await storeService.getMenus() - this.menus = response.data - this.totalCount = response.data.length - return response - } catch (error) { - throw error - } finally { - this.loading = false - } - }, - - async createMenu(menuData) { - try { - this.loading = true - const response = await storeService.createMenu(menuData) - this.menus.push(response.data) - this.totalCount++ - return response - } catch (error) { - throw error - } finally { - this.loading = false - } - }, - - async updateMenu(menuId, menuData) { - try { - this.loading = true - const response = await storeService.updateMenu(menuId, menuData) - const index = this.menus.findIndex(menu => menu.id === menuId) - if (index !== -1) { - this.menus[index] = response.data + // 스토어 서비스 임포트 + 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 + + if (result.message === '등록된 매장이 없습니다') { + return { success: false, message: '등록된 매장이 없습니다' } + } else { + return { success: false, message: result.message || '매장 정보 조회에 실패했습니다' } + } } - return response } catch (error) { - throw 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: '등록된 매장이 없습니다' } + } + + 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 } }, - - async deleteMenu(menuId) { + + /** + * 메뉴 목록 조회 - 추가된 메서드 + */ + async fetchMenus() { + console.log('=== Store 스토어: 메뉴 목록 조회 시작 ===') + try { - this.loading = true - await storeService.deleteMenu(menuId) - this.menus = this.menus.filter(menu => menu.id !== menuId) - this.totalCount-- + // 현재는 목업 데이터 반환 (추후 실제 API 연동 시 수정) + const mockMenus = [ + { + id: 1, + name: '아메리카노', + price: 4000, + category: '커피', + description: '진한 풍미의 아메리카노', + imageUrl: '/images/americano.jpg', + isAvailable: true + }, + { + id: 2, + name: '카페라떼', + price: 4500, + category: '커피', + description: '부드러운 우유가 들어간 라떼', + imageUrl: '/images/latte.jpg', + isAvailable: true + }, + { + id: 3, + name: '치즈케이크', + price: 6000, + category: '디저트', + description: '진한 크림치즈로 만든 케이크', + imageUrl: '/images/cheesecake.jpg', + isAvailable: false + } + ] + + this.menus = mockMenus + console.log('✅ 메뉴 목록 설정 완료:', mockMenus) + + return { success: true, data: mockMenus } } catch (error) { - throw error + console.error('메뉴 목록 조회 실패:', error) + this.menus = [] + return { success: false, message: '메뉴 목록을 불러오는데 실패했습니다' } + } + }, + + /** + * 매장 등록 + */ + async registerStore(storeData) { + console.log('매장 등록 시작:', storeData) + this.loading = true + this.error = null + + try { + const { storeService } = await import('@/services/store') + + const result = await storeService.registerStore(storeData) + + console.log('매장 등록 결과:', result) + + if (result.success) { + // 등록 성공 후 매장 정보 다시 조회 + await this.fetchStoreInfo() + return result + } else { + this.error = result.message + return result + } + } catch (error) { + console.error('매장 등록 실패:', error) + this.error = error.message + return { success: false, message: error.message || '매장 등록에 실패했습니다' } } finally { this.loading = false } + }, + + /** + * 매장 정보 수정 + */ + async updateStore(storeId, storeData) { + console.log('매장 정보 수정 시작:', { storeId, storeData }) + this.loading = true + this.error = null + + try { + const { storeService } = await import('@/services/store') + + const result = await storeService.updateStore(storeId, storeData) + + console.log('매장 수정 결과:', result) + + if (result.success) { + // 수정 성공 후 매장 정보 다시 조회 + await this.fetchStoreInfo() + return result + } else { + this.error = result.message + return result + } + } catch (error) { + console.error('매장 수정 실패:', error) + this.error = error.message + return { success: false, message: error.message || '매장 수정에 실패했습니다' } + } finally { + this.loading = false + } + }, + + /** + * 매장 정보 초기화 + */ + clearStoreInfo() { + this.storeInfo = null + this.menus = [] + this.error = null + this.loading = false } } }) \ No newline at end of file diff --git a/src/store/store.js b/src/store/store.js index 138485c..f20178e 100644 --- a/src/store/store.js +++ b/src/store/store.js @@ -1,202 +1,233 @@ -//* src/store/store.js 수정 - 기존 구조 유지하고 API 연동만 추가 -import { defineStore } from 'pinia' -import { ref, computed } from 'vue' -import storeService from '@/services/store' +//* src/services/store.js - 매장 서비스 완전 수정 +import { storeApi, handleApiError, formatSuccessResponse } from './api.js' -export const useStoreStore = defineStore('store', () => { - // 기존 상태들 유지 - const storeInfo = ref(null) - const menus = ref([]) - const salesData = ref(null) - const isLoading = ref(false) - - // 기존 computed 속성들 유지 - const hasStoreInfo = computed(() => !!storeInfo.value) - const menuCount = computed(() => menus.value?.length || 0) - - // fetchStoreInfo를 실제 API 호출로 수정 - const fetchStoreInfo = async () => { - if (import.meta.env.DEV) { - console.log('개발 모드: 매장 정보 API 호출 건너뛰기') - return { success: true } - } - - isLoading.value = true - +/** + * 매장 관련 API 서비스 + * 백엔드 Store Controller와 연동 (포트 8082) + */ +class StoreService { + /** + * 매장 등록 (STR-015: 매장 등록) + * @param {Object} storeData - 매장 정보 + * @returns {Promise} 매장 등록 결과 + */ + async registerStore(storeData) { try { - const result = await storeService.getStore() + console.log('=== 매장 등록 API 호출 ===') + console.log('요청 데이터:', storeData) - if (result.success) { - storeInfo.value = result.data - return { success: true } - } else { - console.warn('매장 정보 조회 실패:', result.message) - return { success: false, error: result.message } - } - } catch (error) { - console.warn('매장 정보 조회 실패:', error) - return { success: false, error: '네트워크 오류가 발생했습니다.' } - } finally { - isLoading.value = false - } - } - - // saveStoreInfo를 실제 API 호출로 수정 - const saveStoreInfo = async (storeData) => { - isLoading.value = true - - try { - let result - if (storeInfo.value) { - // 기존 매장 정보 수정 - result = await storeService.updateStore(storeData) - } else { - // 새 매장 등록 - result = await storeService.registerStore(storeData) + // 백엔드 StoreCreateRequest에 맞는 형태로 변환 + const requestData = { + storeName: storeData.storeName, + businessType: storeData.businessType, + address: storeData.address, + phoneNumber: storeData.phoneNumber, + businessHours: storeData.businessHours, + closedDays: storeData.closedDays, + seatCount: parseInt(storeData.seatCount) || 0, + instaAccounts: storeData.instaAccounts || '', + blogAccounts: storeData.blogAccounts || '', + description: storeData.description || '' } - if (result.success) { - storeInfo.value = result.data - return { success: true, message: '매장 정보가 저장되었습니다.' } - } else { - return { success: false, error: result.message } - } - } catch (error) { - return { success: false, error: '네트워크 오류가 발생했습니다.' } - } finally { - isLoading.value = false - } - } - - // fetchMenus를 실제 API 호출로 수정 - const fetchMenus = async () => { - if (!storeInfo.value?.storeId) { - console.warn('매장 ID가 없어 메뉴를 조회할 수 없습니다.') - return { success: false, error: '매장 정보가 필요합니다.' } - } - - isLoading.value = true - - try { - const result = await storeService.getMenus(storeInfo.value.storeId) + console.log('=== 각 필드 상세 검증 ===') + console.log('storeName:', requestData.storeName, '(타입:', typeof requestData.storeName, ')') + console.log('businessType:', requestData.businessType, '(타입:', typeof requestData.businessType, ')') + console.log('address:', requestData.address, '(타입:', typeof requestData.address, ')') + console.log('seatCount:', requestData.seatCount, '(타입:', typeof requestData.seatCount, ')') - if (result.success) { - menus.value = result.data - return { success: true } - } else { - return { success: false, error: result.message } - } - } catch (error) { - return { success: false, error: '네트워크 오류가 발생했습니다.' } - } finally { - isLoading.value = false - } - } - - // 메뉴 관련 메서드들 API 연동 추가 - const saveMenu = async (menuData) => { - isLoading.value = true - - try { - const result = await storeService.registerMenu(menuData) + console.log('백엔드 전송 데이터:', requestData) - if (result.success) { - // 메뉴 목록 새로고침 - await fetchMenus() - return { success: true, message: '메뉴가 등록되었습니다.' } - } else { - return { success: false, error: result.message } - } - } catch (error) { - return { success: false, error: '네트워크 오류가 발생했습니다.' } - } finally { - isLoading.value = false - } - } - - const updateMenu = async (menuId, menuData) => { - isLoading.value = true - - try { - const result = await storeService.updateMenu(menuId, menuData) + const response = await storeApi.post('/register', requestData) - if (result.success) { - // 메뉴 목록 새로고침 - await fetchMenus() - return { success: true, message: '메뉴가 수정되었습니다.' } - } else { - return { success: false, error: result.message } - } - } catch (error) { - return { success: false, error: '네트워크 오류가 발생했습니다.' } - } finally { - isLoading.value = false - } - } - - const deleteMenu = async (menuId) => { - isLoading.value = true - - try { - const result = await storeService.deleteMenu(menuId) + console.log('매장 등록 API 응답:', response.data) - if (result.success) { - // 메뉴 목록 새로고침 - await fetchMenus() - return { success: true, message: '메뉴가 삭제되었습니다.' } + // 백엔드 응답 구조에 맞게 처리 + if (response.data && (response.data.status === 200 || response.data.success !== false)) { + return { + success: true, + message: response.data.message || '매장이 등록되었습니다.', + data: response.data.data + } } else { - return { success: false, error: result.message } + throw new Error(response.data.message || '매장 등록에 실패했습니다.') } } catch (error) { - return { success: false, error: '네트워크 오류가 발생했습니다.' } - } finally { - isLoading.value = false - } - } - - // 매출 정보 조회 추가 - const fetchSalesData = async () => { - if (!storeInfo.value?.storeId) { - return { success: false, error: '매장 정보가 필요합니다.' } - } - - isLoading.value = true - - try { - const result = await storeService.getSales(storeInfo.value.storeId) + console.error('매장 등록 실패:', error) - if (result.success) { - salesData.value = result.data - return { success: true } - } else { - return { success: false, error: result.message } + if (error.response) { + console.error('응답 상태:', error.response.status) + console.error('응답 데이터:', error.response.data) } - } catch (error) { - return { success: false, error: '네트워크 오류가 발생했습니다.' } - } finally { - isLoading.value = false + + return handleApiError(error) } } - return { - // 상태 - storeInfo, - menus, - salesData, - isLoading, - - // 컴퓨티드 - hasStoreInfo, - menuCount, - - // 메서드 - fetchStoreInfo, - saveStoreInfo, - fetchMenus, - saveMenu, - updateMenu, - deleteMenu, - fetchSalesData + /** + * 매장 정보 조회 (STR-005: 매장 정보 관리) + * @returns {Promise} 매장 정보 + */ + async getStore() { + try { + console.log('=== 매장 정보 조회 API 호출 ===') + + const response = await storeApi.get('/') + + console.log('매장 정보 조회 API 응답:', response.data) + + // 백엔드 응답 구조에 맞게 처리 + if (response.data && response.data.data) { + return { + success: true, + message: '매장 정보를 조회했습니다.', + data: response.data.data + } + } else if (response.data && response.data.data === null) { + // 매장이 없는 경우 + return { + success: false, + message: '등록된 매장이 없습니다', + data: null + } + } else { + throw new Error(response.data.message || '매장 정보를 찾을 수 없습니다.') + } + } catch (error) { + console.error('매장 정보 조회 실패:', error) + + // 404 오류 처리 (매장이 없음) + if (error.response?.status === 404) { + return { + success: false, + message: '등록된 매장이 없습니다', + data: null + } + } + + // 500 오류 처리 (서버 내부 오류) + if (error.response?.status === 500) { + console.error('서버 내부 오류 - 백엔드 로그 확인 필요:', error.response?.data) + return { + success: false, + message: '서버 오류가 발생했습니다. 관리자에게 문의하세요.', + data: null + } + } + + return handleApiError(error) + } } -}) + /** + * 매장 정보 수정 (STR-010: 매장 수정) + * @param {number} storeId - 매장 ID (현재는 사용하지 않음 - JWT에서 사용자 확인) + * @param {Object} storeData - 수정할 매장 정보 + * @returns {Promise} 매장 수정 결과 + */ + async updateStore(storeId, storeData) { + try { + console.log('=== 매장 정보 수정 API 호출 ===') + console.log('요청 데이터:', storeData) + + // 백엔드 StoreUpdateRequest에 맞는 형태로 변환 + const requestData = { + storeName: storeData.storeName, + businessType: storeData.businessType, + address: storeData.address, + phoneNumber: storeData.phoneNumber, + businessHours: storeData.businessHours, + closedDays: storeData.closedDays, + seatCount: parseInt(storeData.seatCount) || 0, + instaAccounts: storeData.instaAccounts || '', + blogAccounts: storeData.blogAccounts || '', + description: storeData.description || '' + } + + console.log('백엔드 전송 데이터:', requestData) + + // PUT 요청 (storeId는 JWT에서 추출하므로 URL에 포함하지 않음) + const response = await storeApi.put('/', requestData) + + console.log('매장 정보 수정 API 응답:', response.data) + + if (response.data && (response.data.status === 200 || response.data.success !== false)) { + return { + success: true, + message: response.data.message || '매장 정보가 수정되었습니다.', + data: response.data.data + } + } else { + throw new Error(response.data.message || '매장 정보 수정에 실패했습니다.') + } + } catch (error) { + console.error('매장 정보 수정 실패:', error) + return handleApiError(error) + } + } + + /** + * 매출 정보 조회 (STR-020: 대시보드) + * @param {string} period - 조회 기간 (today, week, month, year) + * @returns {Promise} 매출 정보 + */ + async getSales(period = 'today') { + try { + // 현재는 목업 데이터 반환 (추후 실제 API 연동 시 수정) + const mockSalesData = { + todaySales: 150000, + yesterdaySales: 120000, + changeRate: 25.0, + monthlyTarget: 3000000, + achievementRate: 45.2 + } + + return formatSuccessResponse(mockSalesData, '매출 정보를 조회했습니다.') + } catch (error) { + return handleApiError(error) + } + } + + /** + * 메뉴 목록 조회 (개발 예정) + * @returns {Promise} 메뉴 목록 + */ + async getMenus() { + try { + // 현재는 목업 데이터 반환 (추후 실제 API 연동 시 수정) + const mockMenus = [ + { + id: 1, + name: '아메리카노', + price: 4000, + category: '커피', + description: '진한 풍미의 아메리카노', + imageUrl: '/images/americano.jpg', + isAvailable: true + }, + { + id: 2, + name: '카페라떼', + price: 4500, + category: '커피', + description: '부드러운 우유가 들어간 라떼', + imageUrl: '/images/latte.jpg', + isAvailable: true + } + ] + + return formatSuccessResponse(mockMenus, '메뉴 목록을 조회했습니다.') + } catch (error) { + return handleApiError(error) + } + } +} + +// 싱글톤 인스턴스 생성 및 export +export const storeService = new StoreService() +export default storeService + +// 디버깅을 위한 전역 노출 (개발 환경에서만) +if (process.env.NODE_ENV === 'development') { + window.storeService = storeService +} \ No newline at end of file diff --git a/src/views/StoreManagementView.vue b/src/views/StoreManagementView.vue index 7494f3f..adb6713 100644 --- a/src/views/StoreManagementView.vue +++ b/src/views/StoreManagementView.vue @@ -1,4 +1,5 @@ -//* src/views/StoreManagementView.vue +//* src/views/StoreManagementView.vue - 완전한 매장 등록 화면 + @@ -941,51 +534,16 @@ import { ref, computed, onMounted } from 'vue' import { useStoreStore } from '@/store/index' -/** - * AI 마케팅 서비스 - 매장 관리 페이지 - * 매장 정보 관리 및 메뉴 관리 기능 제공 - * 유저스토리: STR-005, STR-010, STR-015, STR-020, STR-025, STR-030, STR-035, STR-040 - */ - -// 스토어 const storeStore = useStoreStore() -// 탭 관리 +// 반응형 상태 const currentTab = ref('basic') - -// 매장 정보 관련 상태 const showCreateDialog = ref(false) const editMode = ref(false) const formValid = ref(false) const saving = ref(false) -const imageInput = ref(null) +const storeFormRef = ref(null) // 폼 참조 -// 메뉴 관리 관련 상태 -const showMenuDialog = ref(false) -const editMenuMode = ref(false) -const menuFormValid = ref(false) -const savingMenu = ref(false) -const deletingMenu = ref(false) -const showDeleteMenuDialog = ref(false) -const deleteMenuTarget = ref(null) -const menuImageInput = ref(null) -const menuSearch = ref('') -const menuCategoryFilter = ref('전체') -const menuStatusFilter = ref('전체') - -// SNS 연동 관련 상태 -const snsCheckLoading = ref({ - instagram: false, - blog: false -}) -const showSnsResultDialog = ref(false) -const snsConnectionResult = ref({ - success: false, - platform: '', - message: '' -}) - -// 스낵바 상태 const snackbar = ref({ show: false, message: '', @@ -996,8 +554,6 @@ const snackbar = ref({ const formData = ref({ storeName: '', businessType: '', - ownerName: '', - businessNumber: '', address: '', phoneNumber: '', seatCount: 0, @@ -1006,111 +562,15 @@ const formData = ref({ openTime: '09:00', closeTime: '21:00', holidays: [], - deliveryAvailable: false, - takeoutAvailable: true, - imageUrl: '' + description: '' }) -// 메뉴 폼 데이터 -const menuFormData = ref({ - menuName: '', - price: 0, - category: '', - description: '', - available: true, - recommended: false, - imageUrl: '' -}) - -// 메뉴 목록 (데모 데이터) -const menus = ref([ - { - id: 1, - menuName: '김치찌개', - category: '찌개류', - price: 8000, - description: '돼지고기와 신김치로 끓인 얼큰한 김치찌개', - available: true, - recommended: true, - imageUrl: '/images/kimchi-jjigae.jpg' - }, - { - id: 2, - menuName: '된장찌개', - category: '찌개류', - price: 7000, - description: '집된장으로 끓인 구수한 된장찌개', - available: true, - recommended: false, - imageUrl: '/images/doenjang-jjigae.jpg' - }, - { - id: 3, - menuName: '제육볶음', - category: '볶음류', - price: 12000, - description: '매콤달콤한 양념에 볶은 제육볶음', - available: false, - recommended: true, - imageUrl: '/images/jeyuk-bokkeum.jpg' - } -]) - -// 컴퓨티드 속성 -const storeInfo = computed(() => storeStore.storeInfo || {}) - -const availableMenusCount = computed(() => - menus.value.filter(menu => menu.available).length -) - -const recommendedMenusCount = computed(() => - menus.value.filter(menu => menu.recommended).length -) - -const averagePrice = computed(() => { - if (menus.value.length === 0) return '0원' - const total = menus.value.reduce((sum, menu) => sum + menu.price, 0) - const average = Math.round(total / menus.value.length) - return formatCurrency(average) -}) - -const filteredMenus = computed(() => { - let filtered = menus.value - - // 검색 필터 - if (menuSearch.value) { - filtered = filtered.filter(menu => - menu.menuName.toLowerCase().includes(menuSearch.value.toLowerCase()) || - menu.description.toLowerCase().includes(menuSearch.value.toLowerCase()) - ) - } - - // 카테고리 필터 - if (menuCategoryFilter.value !== '전체') { - filtered = filtered.filter(menu => menu.category === menuCategoryFilter.value) - } - - // 상태 필터 - if (menuStatusFilter.value !== '전체') { - const isAvailable = menuStatusFilter.value === '판매중' - filtered = filtered.filter(menu => menu.available === isAvailable) - } - - return filtered -}) - -const menuCategories = computed(() => { - const categories = [...new Set(menus.value.map(menu => menu.category))] - return categories.sort() -}) - -// 선택 옵션 +// 선택 옵션들 const businessTypes = [ - '한식', '중식', '일식', '양식', '분식', '치킨', '피자', '버거', - '카페', '디저트', '술집', '기타' + '카페', '레스토랑', '베이커리', '디저트', '주점', '패스트푸드', '기타' ] -const daysOfWeek = [ +const weekDays = [ { title: '월요일', value: 'monday' }, { title: '화요일', value: 'tuesday' }, { title: '수요일', value: 'wednesday' }, @@ -1120,219 +580,87 @@ const daysOfWeek = [ { title: '일요일', value: 'sunday' } ] -// 유효성 검사 규칙 -const businessNumberRules = [ - v => !!v || '사업자등록번호를 입력해주세요', - v => /^\d{3}-\d{2}-\d{5}$/.test(v) || '올바른 사업자등록번호 형식이 아닙니다 (예: 123-45-67890)' -] +// 컴퓨티드 속성 +const storeInfo = computed(() => storeStore.storeInfo || {}) -const phoneRules = [ - v => !!v || '연락처를 입력해주세요', - v => /^[0-9-+\s()]+$/.test(v) || '올바른 연락처 형식이 아닙니다' -] - -const priceRules = [ - v => !!v || '가격을 입력해주세요', - v => v > 0 || '가격은 0보다 커야 합니다' -] - -// 유틸리티 함수 -const formatCurrency = (amount) => { - return new Intl.NumberFormat('ko-KR', { - style: 'currency', - currency: 'KRW' - }).format(amount) -} - -const formatHolidays = (holidays) => { - if (!holidays || holidays.length === 0) return '없음' - const dayNames = { - monday: '월요일', - tuesday: '화요일', - wednesday: '수요일', - thursday: '목요일', - friday: '금요일', - saturday: '토요일', - sunday: '일요일' - } - return holidays.map(day => dayNames[day]).join(', ') -} - -const getCategoryColor = (category) => { - const colors = { - '면류': 'orange', - '튀김류': 'amber', - '음료': 'blue', - '안주': 'green', - '디저트': 'pink', - '찌개류': 'red', - '볶음류': 'purple' - } - return colors[category] || 'grey' -} - -// 메서드 - -/** - * SNS 계정 연동 확인 - * @param {string} platform - SNS 플랫폼 (instagram, blog) - */ -const checkSnsConnection = async (platform) => { - console.log(`${platform} 연동 확인 시작`) +// 유틸리티 함수들 +const formatClosedDays = (closedDays) => { + if (!closedDays) return '미설정' - snsCheckLoading.value[platform] = true + if (typeof closedDays === 'string') { + return closedDays + } - try { - // 개발 모드에서는 빠른 시뮬레이션 - const delay = import.meta.env.DEV ? 1000 : 2000 - await new Promise(resolve => setTimeout(resolve, delay)) - - // 랜덤하게 성공/실패 결정 (실제로는 API 응답에 따라 결정) - const isSuccess = Math.random() > 0.3 - - snsConnectionResult.value.success = isSuccess - snsConnectionResult.value.platform = platform - - if (isSuccess) { - snsConnectionResult.value.message = platform === 'instagram' - ? '인스타그램 계정 연동이 확인되었습니다!' - : '네이버 블로그 연동이 확인되었습니다!' - } else { - snsConnectionResult.value.message = platform === 'instagram' - ? '인스타그램 계정을 찾을 수 없거나 연동할 수 없습니다. URL을 확인해주세요.' - : '네이버 블로그를 찾을 수 없거나 연동할 수 없습니다. URL을 확인해주세요.' + if (Array.isArray(closedDays)) { + const dayNames = { + 'monday': '월요일', + 'tuesday': '화요일', + 'wednesday': '수요일', + 'thursday': '목요일', + 'friday': '금요일', + 'saturday': '토요일', + 'sunday': '일요일' } - showSnsResultDialog.value = true - - } catch (error) { - console.error('SNS 연동 확인 중 오류:', error) - snsConnectionResult.value.success = false - snsConnectionResult.value.message = '연동 확인 중 오류가 발생했습니다. 잠시 후 다시 시도해주세요.' - showSnsResultDialog.value = true - } finally { - snsCheckLoading.value[platform] = false - } -} - -/** - * 데모 매장 데이터 로드 (개발용) - */ -const loadDemoStoreData = () => { - console.log('데모 매장 데이터 로드') - - // 데모 매장 정보 설정 - const demoStoreInfo = { - id: 1, - storeName: '김씨네 분식점', - businessType: '분식', - ownerName: '김점주', - businessNumber: '123-45-67890', - address: '서울특별시 강남구 테헤란로 123', - phoneNumber: '02-1234-5678', - seatCount: 20, - instagramUrl: '@kimfood_bunsik', - blogUrl: 'blog.naver.com/kimfood123', - openTime: '09:00', - closeTime: '21:00', - holidays: ['sunday'], - deliveryAvailable: true, - takeoutAvailable: true, - imageUrl: '/images/store-demo.jpg' + return closedDays.map(day => dayNames[day] || day).join(', ') || '연중무휴' } - // 스토어에 데모 데이터 직접 설정 - storeStore.storeInfo = demoStoreInfo - - showSnackbar('데모 매장 정보가 로드되었습니다', 'success') + return '미설정' +} + +const showSnackbar = (message, color = 'success') => { + snackbar.value.message = message + snackbar.value.color = color + snackbar.value.show = true +} + +// 메서드들 +const openCreateDialog = () => { + console.log('새 매장 등록 다이얼로그 열기') + editMode.value = false + resetForm() + showCreateDialog.value = true } -/** - * 기본 정보 수정 모드로 전환 - */ const editBasicInfo = () => { + console.log('매장 정보 수정 시작') editMode.value = true - // 현재 매장 정보를 폼 데이터에 복사 - Object.assign(formData.value, { - storeName: storeInfo.value.storeName || '', - businessType: storeInfo.value.businessType || '', - ownerName: storeInfo.value.ownerName || '', - businessNumber: storeInfo.value.businessNumber || '', - address: storeInfo.value.address || '', - phoneNumber: storeInfo.value.phoneNumber || '', - seatCount: storeInfo.value.seatCount || 0, - instagramUrl: storeInfo.value.instagramUrl || '', - blogUrl: storeInfo.value.blogUrl || '', - openTime: storeInfo.value.openTime || '09:00', - closeTime: storeInfo.value.closeTime || '21:00', - holidays: storeInfo.value.holidays || [], - deliveryAvailable: storeInfo.value.deliveryAvailable || false, - takeoutAvailable: storeInfo.value.takeoutAvailable || true, - imageUrl: storeInfo.value.imageUrl || '' - }) + // 기존 매장 정보로 폼 데이터 설정 + const store = storeInfo.value + formData.value = { + storeName: store.storeName || '', + businessType: store.businessType || '', + address: store.address || '', + phoneNumber: store.phoneNumber || '', + seatCount: store.seatCount || 0, + instagramUrl: store.instaAccounts || '', + blogUrl: store.blogAccounts || '', + openTime: store.openTime || '09:00', + closeTime: store.closeTime || '21:00', + holidays: store.holidays || [], + description: store.description || '' + } showCreateDialog.value = true } -/** - * 이미지 선택 - */ -const selectImage = () => { - imageInput.value?.click() +const addMenu = () => { + console.log('메뉴 추가 (개발 예정)') + showSnackbar('메뉴 관리 기능은 곧 업데이트 예정입니다', 'info') } -/** - * 이미지 업로드 처리 - */ -const handleImageUpload = (event) => { - const file = event.target.files[0] - if (file) { - // 실제로는 서버에 업로드하고 URL을 받아옴 - const reader = new FileReader() - reader.onload = (e) => { - formData.value.imageUrl = e.target.result - } - reader.readAsDataURL(file) - } -} - -/** - * 매장 정보 저장 - */ -const saveStoreInfo = async () => { - if (!storeForm.value?.validate()) return - - console.log('매장 정보 저장:', storeFormData.value) - - try { - const result = await storeStore.saveStoreInfo(storeFormData.value) - - if (result.success) { - appStore.showSnackbar(result.message || '매장 정보가 저장되었습니다', 'success') - showStoreForm.value = false - } else { - appStore.showSnackbar(result.error || '저장에 실패했습니다', 'error') - } - } catch (error) { - console.error('매장 정보 저장 실패:', error) - appStore.showSnackbar('네트워크 오류가 발생했습니다', 'error') - } -} - -/** - * 다이얼로그 닫기 - */ -const closeDialog = () => { +const cancelForm = () => { + console.log('폼 취소') showCreateDialog.value = false editMode.value = false - - // 폼 데이터 초기화 - Object.assign(formData.value, { + resetForm() +} + +const resetForm = () => { + formData.value = { storeName: '', businessType: '', - ownerName: '', - businessNumber: '', address: '', phoneNumber: '', seatCount: 0, @@ -1341,562 +669,141 @@ const closeDialog = () => { openTime: '09:00', closeTime: '21:00', holidays: [], - deliveryAvailable: false, - takeoutAvailable: true, - imageUrl: '' - }) -} - -// 메뉴 관리 메서드 - -/** - * 메뉴 등록 다이얼로그 열기 - */ -const openCreateMenuDialog = () => { - editMenuMode.value = false - resetMenuForm() - showMenuDialog.value = true -} - -/** - * 메뉴 수정 - */ -const editMenu = (menu) => { - editMenuMode.value = true - Object.assign(menuFormData.value, menu) - showMenuDialog.value = true -} - -/** - * 메뉴 삭제 확인 - */ -const confirmDeleteMenu = (menu) => { - deleteMenuTarget.value = menu - showDeleteMenuDialog.value = true -} - -/** - * 메뉴 이미지 선택 - */ -const selectMenuImage = () => { - menuImageInput.value?.click() -} - -/** - * 메뉴 이미지 업로드 처리 - */ -const handleMenuImageUpload = (event) => { - const file = event.target.files[0] - if (file) { - const reader = new FileReader() - reader.onload = (e) => { - menuFormData.value.imageUrl = e.target.result - } - reader.readAsDataURL(file) + description: '' + } + + // 폼 validation 초기화 + if (storeFormRef.value) { + storeFormRef.value.resetValidation() } } -/** - * 메뉴 저장 - */ -const saveMenu = async () => { - if (!menuForm.value?.validate()) return - - console.log('메뉴 저장:', menuFormData.value) +// 매장 정보 저장 함수 - 완전히 새로 작성 +const saveStoreInfo = async () => { + console.log('=== 매장 정보 저장 시작 ===') + console.log('편집 모드:', editMode.value) + console.log('폼 데이터:', formData.value) + + // 폼 유효성 검사 + if (!storeFormRef.value) { + console.error('폼 참조를 찾을 수 없습니다') + showSnackbar('폼 오류가 발생했습니다', 'error') + return + } + + const { valid } = await storeFormRef.value.validate() + if (!valid) { + console.log('폼 유효성 검사 실패') + showSnackbar('필수 정보를 모두 입력해주세요', 'error') + return + } + + saving.value = true try { + // 백엔드에 보낼 데이터 형식으로 변환 + const storeData = { + storeName: formData.value.storeName, + businessType: formData.value.businessType, + address: formData.value.address, + phoneNumber: formData.value.phoneNumber, + businessHours: `${formData.value.openTime}-${formData.value.closeTime}`, + closedDays: Array.isArray(formData.value.holidays) ? formData.value.holidays.join(',') : '', + seatCount: parseInt(formData.value.seatCount) || 0, + instaAccounts: formData.value.instagramUrl || '', + blogAccounts: formData.value.blogUrl || '', + description: formData.value.description || '' + } + + console.log('백엔드로 전송할 데이터:', storeData) + let result - if (isMenuEdit.value && editingMenuId.value) { - result = await storeStore.updateMenu(editingMenuId.value, menuFormData.value) + if (editMode.value) { + // 매장 정보 수정 + result = await storeStore.updateStore(storeInfo.value.storeId, storeData) } else { - result = await storeStore.saveMenu(menuFormData.value) + // 새 매장 등록 + result = await storeStore.registerStore(storeData) } - if (result.success) { - appStore.showSnackbar(result.message || '메뉴가 저장되었습니다', 'success') - showMenuForm.value = false - resetMenuForm() - } else { - appStore.showSnackbar(result.error || '저장에 실패했습니다', 'error') - } - } catch (error) { - console.error('메뉴 저장 실패:', error) - appStore.showSnackbar('네트워크 오류가 발생했습니다', 'error') - } -} - -/** - * 메뉴 삭제 - */ -const deleteMenu = async (menuId) => { - try { - const result = await storeStore.deleteMenu(menuId) + console.log('저장 결과:', result) if (result.success) { - appStore.showSnackbar(result.message || '메뉴가 삭제되었습니다', 'success') + showSnackbar( + editMode.value ? '매장 정보가 수정되었습니다' : '매장이 등록되었습니다', + 'success' + ) + showCreateDialog.value = false + editMode.value = false + resetForm() + + // 매장 정보 다시 조회 + await storeStore.fetchStoreInfo() } else { - appStore.showSnackbar(result.error || '삭제에 실패했습니다', 'error') + showSnackbar(result.message || '저장에 실패했습니다', 'error') } } catch (error) { - console.error('메뉴 삭제 실패:', error) - appStore.showSnackbar('네트워크 오류가 발생했습니다', 'error') + console.error('매장 정보 저장 중 오류:', error) + showSnackbar('저장 중 오류가 발생했습니다', 'error') + } finally { + saving.value = false } } -/** - * 메뉴 다이얼로그 닫기 - */ -const closeMenuDialog = () => { - showMenuDialog.value = false - editMenuMode.value = false - resetMenuForm() -} - -/** - * 메뉴 폼 초기화 - */ -const resetMenuForm = () => { - Object.assign(menuFormData.value, { - menuName: '', - price: 0, - category: '', - description: '', - available: true, - recommended: false, - imageUrl: '' - }) -} - -/** - * 필터 초기화 - */ -const clearFilters = () => { - menuSearch.value = '' - menuCategoryFilter.value = '전체' - menuStatusFilter.value = '전체' -} - -/** - * 스낵바 표시 - */ -const showSnackbar = (message, color = 'success') => { - snackbar.value.message = message - snackbar.value.color = color - snackbar.value.show = true -} - -// 메뉴 상세 다이얼로그 관련 상태 (기존 상태들에 추가) -const showMenuDetailDialog = ref(false) -const selectedMenuDetail = ref(null) - -// 메뉴 관리 메서드에 추가할 함수들 - -/** - * 메뉴 상세보기 - */ -const viewMenuDetail = (menu) => { - selectedMenuDetail.value = { ...menu } - showMenuDetailDialog.value = true -} - -/** - * 상세화면에서 수정 모드로 전환 - */ -const editFromDetail = () => { - showMenuDetailDialog.value = false - editMenuMode.value = true - Object.assign(menuFormData.value, selectedMenuDetail.value) - showMenuDialog.value = true -} - /** * 컴포넌트 마운트 시 실행 */ onMounted(async () => { - console.log('StoreManagementView 마운트됨') + console.log('=== StoreManagementView 마운트됨 ===') try { - // 매장 정보 로드 - if (!storeStore.hasStoreInfo) { - await storeStore.fetchStoreInfo() + // 매장 정보 조회 + const result = await storeStore.fetchStoreInfo() + + console.log('매장 정보 조회 결과:', result) + + if (result.success) { + console.log('✅ 매장 정보 로드 완료:', result.data) + + // 메뉴 관리 탭에서 사용할 메뉴 정보도 로드 + try { + await storeStore.fetchMenus() + console.log('메뉴 정보 로드 완료') + } catch (menuError) { + console.log('메뉴 정보 로드 실패 (개발 중이므로 무시):', menuError) + } + } else { + if (result.message === '등록된 매장이 없습니다') { + console.log('⚠️ 등록된 매장이 없음 - 등록 화면 표시') + // 매장이 없는 경우는 정상적인 상황이므로 에러 메시지 표시하지 않음 + } else { + console.warn('❌ 매장 정보 조회 실패:', result.message) + showSnackbar(result.message || '매장 정보를 불러오는데 실패했습니다', 'error') + } } - - // 메뉴 목록 로드 - await storeStore.fetchMenus() - } catch (error) { - console.warn('매장 관리 데이터 로드 실패 (개발 중이므로 무시):', error) + console.error('매장 정보 조회 중 예외 발생:', error) + showSnackbar('매장 정보를 불러오는 중 오류가 발생했습니다', 'error') } }) \ No newline at end of file