source merge

This commit is contained in:
unknown 2025-06-17 14:14:06 +09:00
parent 55025dad57
commit 43595d5e72
6 changed files with 1039 additions and 518 deletions

View File

@ -6,7 +6,6 @@ const getApiUrls = () => {
const config = window.__runtime_config__ || {} const config = window.__runtime_config__ || {}
return { return {
GATEWAY_URL: config.GATEWAY_URL || 'http://20.1.2.3', GATEWAY_URL: config.GATEWAY_URL || 'http://20.1.2.3',
<<<<<<< HEAD
AUTH_URL: config.AUTH_URL || 'http://localhost:8081/api/auth', AUTH_URL: config.AUTH_URL || 'http://localhost:8081/api/auth',
MEMBER_URL: config.MEMBER_URL || 'http://localhost:8081/api/member', MEMBER_URL: config.MEMBER_URL || 'http://localhost:8081/api/member',
STORE_URL: config.STORE_URL || 'http://localhost:8082/api/store', STORE_URL: config.STORE_URL || 'http://localhost:8082/api/store',
@ -16,7 +15,6 @@ const getApiUrls = () => {
SALES_URL: config.SALES_URL || 'http://localhost:8082/api/sales', SALES_URL: config.SALES_URL || 'http://localhost:8082/api/sales',
// ⚠️ 수정: 추천 API는 ai-recommend 서비스 (포트 8084) // ⚠️ 수정: 추천 API는 ai-recommend 서비스 (포트 8084)
RECOMMEND_URL: config.RECOMMEND_URL || 'http://localhost:8084/api/recommendations', RECOMMEND_URL: config.RECOMMEND_URL || 'http://localhost:8084/api/recommendations',
=======
AUTH_URL: 'http://localhost:8081/api/auth', AUTH_URL: 'http://localhost:8081/api/auth',
MEMBER_URL: 'http://localhost:8081/api/member', MEMBER_URL: 'http://localhost:8081/api/member',
STORE_URL: config.STORE_URL || 'http://localhost:8082/api/store', STORE_URL: config.STORE_URL || 'http://localhost:8082/api/store',
@ -24,7 +22,6 @@ const getApiUrls = () => {
MENU_URL: config.MENU_URL || 'http://localhost:8082/api/menu', MENU_URL: config.MENU_URL || 'http://localhost:8082/api/menu',
SALES_URL: config.SALES_URL || 'http://localhost:8082/api/sales', SALES_URL: config.SALES_URL || 'http://localhost:8082/api/sales',
RECOMMEND_URL: config.RECOMMEND_URL || 'http://20.1.2.3/api/recommendation', RECOMMEND_URL: config.RECOMMEND_URL || 'http://20.1.2.3/api/recommendation',
>>>>>>> 87871709f2bbcdab5df004a3954d6ba0af3cadce
} }
} }

View File

@ -1,5 +1,5 @@
// src/services/menu.js - 메뉴 관련 API 서비스 //* src/services/menu.js - 백엔드 수정 없이 프론트엔드만 수정
import { menuApi, handleApiError, formatSuccessResponse } from './api.js' import { menuApi, apiWithImage, handleApiError, formatSuccessResponse } from './api.js'
/** /**
* 메뉴 관련 API 서비스 * 메뉴 관련 API 서비스
@ -48,42 +48,7 @@ class MenuService {
} }
/** /**
* 메뉴 상세 조회 * 메뉴 등록 (createMenu)
* @param {number} menuId - 메뉴 ID
* @returns {Promise<Object>} 메뉴 상세 정보
*/
async getMenuDetail(menuId) {
try {
console.log('=== 메뉴 상세 조회 API 호출 ===')
console.log('메뉴 ID:', menuId)
if (!menuId || menuId === 'undefined') {
throw new Error('올바른 메뉴 ID가 필요합니다')
}
const numericMenuId = parseInt(menuId)
if (isNaN(numericMenuId)) {
throw new Error('메뉴 ID는 숫자여야 합니다')
}
// GET /api/menu/{menuId}
const response = await menuApi.get(`/${numericMenuId}`)
console.log('메뉴 상세 조회 API 응답:', response.data)
if (response.data && response.data.status === 200) {
return formatSuccessResponse(response.data.data, '메뉴 상세 정보를 조회했습니다.')
} else {
throw new Error(response.data.message || '메뉴를 찾을 수 없습니다.')
}
} catch (error) {
console.error('메뉴 상세 조회 실패:', error)
return handleApiError(error)
}
}
/**
* 메뉴 등록
* @param {Object} menuData - 메뉴 정보 * @param {Object} menuData - 메뉴 정보
* @returns {Promise<Object>} 등록 결과 * @returns {Promise<Object>} 등록 결과
*/ */
@ -94,7 +59,7 @@ class MenuService {
const requestData = { const requestData = {
storeId: menuData.storeId, storeId: menuData.storeId,
menuName: menuData.menuName, menuName: menuData.menuName || menuData.name,
category: menuData.category, category: menuData.category,
price: parseInt(menuData.price) || 0, price: parseInt(menuData.price) || 0,
description: menuData.description || '' description: menuData.description || ''
@ -119,47 +84,163 @@ class MenuService {
} }
/** /**
* 메뉴 수정 * 메뉴 등록 (registerMenu 별칭)
* @param {number} menuId - 메뉴 ID * @param {Object} menuData - 메뉴 정보
* @param {Object} menuData - 수정할 메뉴 정보 * @returns {Promise<Object>} 등록 결과
* @returns {Promise<Object>} 수정 결과
*/ */
async updateMenu(menuId, menuData) { async registerMenu(menuData) {
return await this.createMenu(menuData)
}
/**
* 메뉴 수정
* @param {number} menuId - 메뉴 ID
* @param {Object} menuData - 수정할 메뉴 정보
* @returns {Promise<Object>} 수정 결과
*/
async updateMenu(menuId, menuData) {
try {
console.log('=== 메뉴 수정 API 호출 ===')
console.log('메뉴 ID:', menuId, '타입:', typeof menuId)
console.log('원본 수정 데이터:', menuData)
if (!menuId || menuId === 'undefined') {
throw new Error('올바른 메뉴 ID가 필요합니다')
}
const numericMenuId = parseInt(menuId)
if (isNaN(numericMenuId)) {
throw new Error('메뉴 ID는 숫자여야 합니다')
}
// 데이터 검증 및 정리
const menuName = menuData.menuName || menuData.name
const category = menuData.category
const price = menuData.price
const description = menuData.description || ''
// 필수 필드 검증
if (!menuName || !category || price === undefined || price === null) {
console.error('필수 필드 누락:', { menuName, category, price, description })
throw new Error('메뉴명, 카테고리, 가격은 필수 입력 사항입니다')
}
// 가격 검증 (숫자이고 0 이상)
const numericPrice = parseInt(price)
if (isNaN(numericPrice) || numericPrice < 0) {
throw new Error('올바른 가격을 입력해주세요')
}
// 백엔드 MenuUpdateRequest DTO에 맞는 데이터 구조
const requestData = {
menuName: menuName.trim(),
category: category.trim(),
price: numericPrice,
description: description.trim()
}
console.log('검증된 백엔드 전송 데이터:', requestData)
console.log('✅ 검증된 메뉴 ID:', numericMenuId)
// PUT /api/menu/{menuId}
const response = await menuApi.put(`/${numericMenuId}`, requestData)
console.log('메뉴 수정 API 응답:', response.data)
if (response.data && response.data.status === 200) {
return formatSuccessResponse(response.data.data, '메뉴가 성공적으로 수정되었습니다.')
} else {
throw new Error(response.data.message || '메뉴 수정에 실패했습니다.')
}
} catch (error) {
console.error('메뉴 수정 실패:', error)
// HTTP 응답 에러 상세 디버깅
if (error.response) {
console.error('=== HTTP 응답 에러 상세 ===')
console.error('상태 코드:', error.response.status)
console.error('상태 텍스트:', error.response.statusText)
console.error('응답 데이터:', error.response.data)
console.error('요청 URL:', error.config?.url)
console.error('요청 메서드:', error.config?.method)
console.error('요청 데이터:', error.config?.data)
// 400 에러 (잘못된 요청) 처리
if (error.response.status === 400) {
const errorMessage = error.response.data?.message || '입력 데이터가 올바르지 않습니다.'
console.error('백엔드 validation 에러:', errorMessage)
return {
success: false,
message: errorMessage,
error: error.response.data
}
}
// 500 오류 처리
if (error.response.status === 500) {
const errorMessage = error.response.data?.message || '서버 내부 오류가 발생했습니다.'
console.error('백엔드 에러 메시지:', errorMessage)
return {
success: false,
message: errorMessage,
error: error.response.data
}
}
}
return handleApiError(error)
}
}
/**
* 메뉴 이미지 업로드
* @param {number} menuId - 메뉴 ID
* @param {File} file - 이미지 파일
* @returns {Promise<Object>} 업로드 결과
*/
async uploadMenuImage(menuId, file) {
try { try {
console.log('=== 메뉴 수정 API 호출 ===') console.log('=== 메뉴 이미지 업로드 API 호출 ===')
console.log('메뉴 ID:', menuId) console.log('메뉴 ID:', menuId, '파일:', file?.name)
console.log('수정 데이터:', menuData)
if (!menuId || menuId === 'undefined') { if (!menuId || menuId === 'undefined') {
throw new Error('올바른 메뉴 ID가 필요합니다') throw new Error('올바른 메뉴 ID가 필요합니다')
} }
if (!file) {
throw new Error('업로드할 파일이 필요합니다')
}
const numericMenuId = parseInt(menuId) const numericMenuId = parseInt(menuId)
if (isNaN(numericMenuId)) { if (isNaN(numericMenuId)) {
throw new Error('메뉴 ID는 숫자여야 합니다') throw new Error('메뉴 ID는 숫자여야 합니다')
} }
const requestData = { // FormData 생성
menuName: menuData.menuName, const formData = new FormData()
category: menuData.category, formData.append('file', file)
price: parseInt(menuData.price) || 0,
description: menuData.description || ''
}
console.log('백엔드 전송 데이터:', requestData) console.log('이미지 업로드 요청 - 메뉴 ID:', numericMenuId)
// PUT /api/menu/{menuId} // POST /api/images/menu/{menuId}
const response = await menuApi.put(`/${numericMenuId}`, requestData) const response = await apiWithImage.post(`/images/menu/${numericMenuId}`, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
console.log('메뉴 수정 API 응답:', response.data) console.log('메뉴 이미지 업로드 API 응답:', response.data)
if (response.data && response.data.status === 200) { if (response.data && (response.data.status === 200 || response.data.success !== false)) {
return formatSuccessResponse(response.data.data, '메뉴가 성공적으로 수정되었습니다.') return formatSuccessResponse(response.data.data || response.data, '메뉴 이미지가 업로드되었습니다.')
} else { } else {
throw new Error(response.data.message || '메뉴 수정에 실패했습니다.') throw new Error(response.data.message || '이미지 업로드에 실패했습니다.')
} }
} catch (error) { } catch (error) {
console.error('메뉴 수정 실패:', error) console.error('메뉴 이미지 업로드 실패:', error)
return handleApiError(error) return handleApiError(error)
} }
} }
@ -208,3 +289,221 @@ export default menuService
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
window.menuService = menuService window.menuService = menuService
} }
//* src/views/StoreManagementView.vue의 수정된 스크립트 부분
// <script setup> 내부의 메뉴 관련 함수들만 수정
// 메뉴 상세 보기 함수 - 메뉴 목록 데이터 활용
const viewMenuDetail = (menu) => {
console.log('=== 메뉴 상세 보기 호출 ===')
console.log('전달받은 메뉴 객체:', menu)
// 메뉴 ID 추출 (여러 형태 지원)
const menuId = menu.menuId || menu.id
if (!menuId) {
console.error('❌ 메뉴 ID를 찾을 수 없음:', menu)
showSnackbar('메뉴 정보가 올바르지 않습니다', 'error')
return
}
console.log('✅ 사용할 메뉴 ID:', menuId)
// API 호출 없이 바로 메뉴 목록의 데이터 사용
selectedMenu.value = {
...menu,
// 호환성을 위해 여러 형태 지원
id: menuId,
menuId: menuId,
name: menu.menuName || menu.name,
menuName: menu.menuName || menu.name
}
console.log('✅ 메뉴 상세 정보 설정 완료:', selectedMenu.value)
showMenuDetailDialog.value = true
}
// 메뉴 수정 함수
const editMenu = (menu) => {
console.log('=== 메뉴 수정 호출 ===')
console.log('전달받은 메뉴 객체:', menu)
// 메뉴 ID 추출 및 검증
const menuId = menu.menuId || menu.id
console.log('추출된 메뉴 ID:', menuId, '타입:', typeof menuId)
if (!menuId) {
console.error('❌ 메뉴 ID를 찾을 수 없음')
showSnackbar('메뉴 정보가 올바르지 않습니다', 'error')
return
}
console.log('✅ 사용할 메뉴 ID:', menuId)
// 수정 모드로 설정
menuEditMode.value = true
// 폼 데이터 설정 (여러 필드명 지원)
menuFormData.value = {
menuId: menuId,
id: menuId, // 호환성
menuName: menu.menuName || menu.name,
name: menu.menuName || menu.name, // 호환성
category: menu.category,
price: menu.price,
description: menu.description || '',
imageUrl: menu.imageUrl
}
console.log('✅ 수정 폼 데이터 설정 완료:', menuFormData.value)
// 다이얼로그 표시
showMenuDialog.value = true
}
// 메뉴 상세에서 수정 버튼 클릭
const editFromDetail = () => {
console.log('=== 메뉴 상세에서 수정 버튼 클릭 ===')
console.log('selectedMenu.value:', selectedMenu.value)
if (!selectedMenu.value) {
showSnackbar('선택된 메뉴가 없습니다', 'error')
return
}
// 메뉴 ID 검증
const menuId = selectedMenu.value.menuId || selectedMenu.value.id
if (!menuId) {
showSnackbar('메뉴 ID를 찾을 수 없습니다', 'error')
return
}
console.log('✅ 수정할 메뉴 정보:', {
id: menuId,
name: selectedMenu.value.menuName || selectedMenu.value.name,
category: selectedMenu.value.category
})
// 상세 다이얼로그 닫기
closeMenuDetail()
// 수정 모드로 전환
editMenu(selectedMenu.value)
}
// 상세 다이얼로그 닫기
const closeMenuDetail = () => {
console.log('=== 메뉴 상세 다이얼로그 닫기 ===')
showMenuDetailDialog.value = false
selectedMenu.value = null
}
// 메뉴 저장 함수 - 이미지 업로드 분리
const saveMenuWithImage = async () => {
if (saving.value) return
console.log('=== 메뉴 저장 + 이미지 업로드 시작 ===')
saving.value = true
try {
// 메뉴 서비스 임포트
const { menuService } = await import('@/services/menu')
let menuResult
if (menuEditMode.value) {
// 메뉴 수정 - PUT /api/menu/{menuId}
const menuId = menuFormData.value.id || menuFormData.value.menuId
if (!menuId) {
showSnackbar('메뉴 ID가 없습니다', 'error')
return
}
console.log('메뉴 수정 API 호출, 메뉴 ID:', menuId)
// 메뉴 데이터 준비
const menuData = {
menuName: menuFormData.value.menuName || menuFormData.value.name,
category: menuFormData.value.category,
price: menuFormData.value.price,
description: menuFormData.value.description || ''
}
menuResult = await menuService.updateMenu(menuId, menuData)
} else {
// 새 메뉴 등록 - POST /api/menu/register
const storeId = storeInfo.value?.storeId
if (!storeId) {
showSnackbar('매장 정보를 찾을 수 없습니다', 'error')
return
}
// 메뉴 데이터 준비 (매장 ID 포함)
const menuData = {
storeId: storeId,
menuName: menuFormData.value.menuName || menuFormData.value.name,
category: menuFormData.value.category,
price: menuFormData.value.price,
description: menuFormData.value.description || ''
}
console.log('메뉴 등록 API 호출, 매장 ID:', storeId)
menuResult = await menuService.createMenu(menuData)
}
console.log('✅ 메뉴 저장 완료:', menuResult)
if (!menuResult.success) {
showSnackbar(menuResult.message || '메뉴 저장에 실패했습니다', 'error')
return
}
// 메뉴 저장 성공 후 이미지 업로드
let imageResult = { success: true }
if (selectedImageFile.value) {
console.log('=== 이미지 업로드 시작 ===')
// 등록된 메뉴의 ID 가져오기
const menuId = menuEditMode.value
? (menuFormData.value.id || menuFormData.value.menuId)
: menuResult.data?.menuId
if (menuId) {
console.log('이미지 업로드 - 메뉴 ID:', menuId)
imageResult = await menuService.uploadMenuImage(menuId, selectedImageFile.value)
console.log('이미지 업로드 결과:', imageResult)
if (!imageResult.success) {
console.warn('이미지 업로드는 실패했지만 메뉴는 저장됨')
showSnackbar('메뉴는 저장되었지만 이미지 업로드에 실패했습니다', 'warning')
}
} else {
console.warn('메뉴 ID를 찾을 수 없어 이미지 업로드 생략')
}
}
// 성공 메시지
if (menuResult.success && imageResult.success) {
showSnackbar(
menuEditMode.value ? '메뉴가 수정되었습니다' : '메뉴가 등록되었습니다',
'success'
)
}
// 다이얼로그 닫기 및 초기화
showMenuDialog.value = false
menuEditMode.value = false
resetMenuForm()
// 메뉴 목록 새로고침
await loadMenus()
} catch (error) {
console.error('메뉴 저장 중 오류:', error)
showSnackbar('저장 중 오류가 발생했습니다', 'error')
} finally {
saving.value = false
}
}

View File

@ -1,18 +1,9 @@
<<<<<<< HEAD
//* src/services/store.js - 매출 API 수정버전
import { storeApi, salesApi, handleApiError, formatSuccessResponse } from './api.js'
/**
* 매장 관련 API 서비스
* 유저스토리: STR-005, STR-010, STR-015, STR-020, STR-025, STR-030, STR-035, STR-040
=======
//* src/services/store.js - 매장 서비스 완전 수정 //* src/services/store.js - 매장 서비스 완전 수정
import { storeApi, handleApiError, formatSuccessResponse } from './api.js' import { storeApi, handleApiError, formatSuccessResponse } from './api.js'
/** /**
* 매장 관련 API 서비스 * 매장 관련 API 서비스
* 백엔드 Store Controller와 연동 (포트 8082) * 백엔드 Store Controller와 연동 (포트 8082)
>>>>>>> 87871709f2bbcdab5df004a3954d6ba0af3cadce
*/ */
class StoreService { class StoreService {
/** /**
@ -28,22 +19,9 @@ class StoreService {
// 백엔드 StoreCreateRequest에 맞는 형태로 변환 // 백엔드 StoreCreateRequest에 맞는 형태로 변환
const requestData = { const requestData = {
storeName: storeData.storeName, storeName: storeData.storeName,
storeImage: storeData.storeImage,
businessType: storeData.businessType, businessType: storeData.businessType,
address: storeData.address, address: storeData.address,
phoneNumber: storeData.phoneNumber, phoneNumber: storeData.phoneNumber,
<<<<<<< HEAD
businessNumber: storeData.businessNumber,
instaAccounts: storeData.instaAccounts,
blogAccounts: storeData.blogAccounts,
businessHours: storeData.businessHours,
closedDays: storeData.closedDays,
seatCount: storeData.seatCount,
description: storeData.description,
})
return formatSuccessResponse(response.data.data, '매장이 등록되었습니다.')
=======
businessHours: storeData.businessHours, businessHours: storeData.businessHours,
closedDays: storeData.closedDays, closedDays: storeData.closedDays,
seatCount: parseInt(storeData.seatCount) || 0, seatCount: parseInt(storeData.seatCount) || 0,
@ -74,7 +52,6 @@ class StoreService {
} else { } else {
throw new Error(response.data.message || '매장 등록에 실패했습니다.') throw new Error(response.data.message || '매장 등록에 실패했습니다.')
} }
>>>>>>> 87871709f2bbcdab5df004a3954d6ba0af3cadce
} catch (error) { } catch (error) {
console.error('매장 등록 실패:', error) console.error('매장 등록 실패:', error)
@ -154,11 +131,6 @@ class StoreService {
*/ */
async updateStore(storeId, storeData) { async updateStore(storeId, storeData) {
try { try {
<<<<<<< HEAD
const response = await storeApi.put('/', storeData)
return formatSuccessResponse(response.data.data, '매장 정보가 수정되었습니다.')
=======
console.log('=== 매장 정보 수정 API 호출 ===') console.log('=== 매장 정보 수정 API 호출 ===')
console.log('요청 데이터:', storeData) console.log('요청 데이터:', storeData)
@ -192,7 +164,6 @@ class StoreService {
} else { } else {
throw new Error(response.data.message || '매장 정보 수정에 실패했습니다.') throw new Error(response.data.message || '매장 정보 수정에 실패했습니다.')
} }
>>>>>>> 87871709f2bbcdab5df004a3954d6ba0af3cadce
} catch (error) { } catch (error) {
console.error('매장 정보 수정 실패:', error) console.error('매장 정보 수정 실패:', error)
return handleApiError(error) return handleApiError(error)
@ -201,32 +172,11 @@ class StoreService {
/** /**
* 매출 정보 조회 (STR-020: 대시보드) * 매출 정보 조회 (STR-020: 대시보드)
<<<<<<< HEAD
* 수정: salesApi 사용하고 storeId 매개변수 추가
* @param {number} storeId - 매장 ID
=======
* @param {string} period - 조회 기간 (today, week, month, year) * @param {string} period - 조회 기간 (today, week, month, year)
>>>>>>> 87871709f2bbcdab5df004a3954d6ba0af3cadce
* @returns {Promise<Object>} 매출 정보 * @returns {Promise<Object>} 매출 정보
*/ */
async getSales(period = 'today') { async getSales(period = 'today') {
try { try {
<<<<<<< HEAD
// storeId가 없으면 먼저 매장 정보를 조회해서 storeId를 가져옴
if (!storeId) {
const storeResponse = await this.getStore()
if (storeResponse.success && storeResponse.data.storeId) {
storeId = storeResponse.data.storeId
} else {
throw new Error('매장 정보를 찾을 수 없습니다.')
}
}
// Sales API 호출 (Store 서비스의 /api/sales/{storeId} 엔드포인트)
const response = await salesApi.get(`/${storeId}`)
return formatSuccessResponse(response.data.data, '매출 정보를 조회했습니다.')
=======
// 현재는 목업 데이터 반환 (추후 실제 API 연동 시 수정) // 현재는 목업 데이터 반환 (추후 실제 API 연동 시 수정)
const mockSalesData = { const mockSalesData = {
todaySales: 150000, todaySales: 150000,
@ -237,7 +187,6 @@ class StoreService {
} }
return formatSuccessResponse(mockSalesData, '매출 정보를 조회했습니다.') return formatSuccessResponse(mockSalesData, '매출 정보를 조회했습니다.')
>>>>>>> 87871709f2bbcdab5df004a3954d6ba0af3cadce
} catch (error) { } catch (error) {
console.error('매출 정보 조회 실패:', error) console.error('매출 정보 조회 실패:', error)
return handleApiError(error) return handleApiError(error)
@ -245,89 +194,11 @@ class StoreService {
} }
/** /**
<<<<<<< HEAD
* 메뉴 등록 (STR-030: 메뉴 등록)
* 수정: 올바른 API 경로 사용
* @param {Object} menuData - 메뉴 정보
* @returns {Promise<Object>} 메뉴 등록 결과
*/
async registerMenu(menuData) {
try {
// Store 서비스의 Menu API 사용
const response = await storeApi.post('/menu/register', {
storeId: menuData.storeId,
menuName: menuData.menuName,
category: menuData.category,
price: menuData.price,
description: menuData.description,
})
return formatSuccessResponse(response.data.data, '메뉴가 등록되었습니다.')
} catch (error) {
return handleApiError(error)
}
}
/**
* 메뉴 목록 조회 (STR-025: 메뉴 조회)
* @param {number} storeId - 매장 ID
=======
* 메뉴 목록 조회 (개발 예정) * 메뉴 목록 조회 (개발 예정)
>>>>>>> 87871709f2bbcdab5df004a3954d6ba0af3cadce
* @returns {Promise<Object>} 메뉴 목록 * @returns {Promise<Object>} 메뉴 목록
*/ */
async getMenus() { async getMenus() {
try { try {
<<<<<<< HEAD
const response = await storeApi.get(`/menu?storeId=${storeId}`)
return formatSuccessResponse(response.data.data, '메뉴 목록을 조회했습니다.')
} catch (error) {
return handleApiError(error)
}
}
/**
* 메뉴 수정 (STR-035: 메뉴 수정)
* @param {number} menuId - 메뉴 ID
* @param {Object} menuData - 수정할 메뉴 정보
* @returns {Promise<Object>} 메뉴 수정 결과
*/
async updateMenu(menuId, menuData) {
try {
const response = await storeApi.put(`/menu/${menuId}`, menuData)
return formatSuccessResponse(response.data.data, '메뉴가 수정되었습니다.')
} catch (error) {
return handleApiError(error)
}
}
/**
* 메뉴 삭제 (STR-040: 메뉴 삭제)
* @param {number} menuId - 메뉴 ID
* @returns {Promise<Object>} 메뉴 삭제 결과
*/
async deleteMenu(menuId) {
try {
await storeApi.delete(`/menu/${menuId}`)
return formatSuccessResponse(null, '메뉴가 삭제되었습니다.')
} catch (error) {
return handleApiError(error)
}
}
/**
* 매장 통계 정보 조회
* @returns {Promise<Object>} 매장 통계
*/
async getStoreStatistics() {
try {
const response = await storeApi.get('/statistics')
return formatSuccessResponse(response.data.data, '매장 통계를 조회했습니다.')
=======
// 현재는 목업 데이터 반환 (추후 실제 API 연동 시 수정) // 현재는 목업 데이터 반환 (추후 실제 API 연동 시 수정)
const mockMenus = [ const mockMenus = [
{ {
@ -351,7 +222,6 @@ class StoreService {
] ]
return formatSuccessResponse(mockMenus, '메뉴 목록을 조회했습니다.') return formatSuccessResponse(mockMenus, '메뉴 목록을 조회했습니다.')
>>>>>>> 87871709f2bbcdab5df004a3954d6ba0af3cadce
} catch (error) { } catch (error) {
return handleApiError(error) return handleApiError(error)
} }

View File

@ -1,4 +1,4 @@
//* src/store/index.js - Store 스토어 수정 (fetchMenus 메서드 추가) //* src/store/index.js - Store 스토어 (완전한 버전)
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
export const useStoreStore = defineStore('store', { export const useStoreStore = defineStore('store', {
@ -80,7 +80,7 @@ export const useStoreStore = defineStore('store', {
}, },
/** /**
* 메뉴 목록 조회 - 실제 API 연동 (매장 ID 필요) * 메뉴 목록 조회 - 실제 API 연동 (매장 ID 필요) - ID 필드 보장
*/ */
async fetchMenus() { async fetchMenus() {
console.log('=== Store 스토어: 메뉴 목록 조회 시작 ===') console.log('=== Store 스토어: 메뉴 목록 조회 시작 ===')
@ -106,10 +106,34 @@ export const useStoreStore = defineStore('store', {
console.log('Result.message:', result.message) console.log('Result.message:', result.message)
if (result.success && result.data) { if (result.success && result.data) {
// ✅ 메뉴 데이터 ID 필드 보장 처리
const menusWithId = (result.data || []).map(menu => {
// ID 필드가 확실히 있도록 보장
const menuId = menu.menuId || menu.id
if (!menuId) {
console.warn('⚠️ 메뉴 ID가 없는 항목 발견:', menu)
}
return {
...menu,
id: menuId, // ✅ id 필드 확실히 설정
menuId: menuId, // ✅ menuId 필드도 설정
// 기타 필드들 보장
menuName: menu.menuName || menu.name || '이름 없음',
category: menu.category || '기타',
price: menu.price || 0,
description: menu.description || '',
available: menu.available !== undefined ? menu.available : true,
recommended: menu.recommended !== undefined ? menu.recommended : false,
imageUrl: menu.imageUrl || '/images/menu-placeholder.png'
}
})
// 메뉴 목록이 있는 경우 // 메뉴 목록이 있는 경우
console.log('✅ 메뉴 목록 설정:', result.data) console.log('✅ 메뉴 목록 설정 (ID 보장됨):', menusWithId)
this.menus = result.data this.menus = menusWithId
return { success: true, data: result.data } return { success: true, data: menusWithId }
} else { } else {
// 메뉴가 없거나 조회 실패한 경우 // 메뉴가 없거나 조회 실패한 경우
console.log('⚠️ 메뉴 목록 없음 또는 조회 실패') console.log('⚠️ 메뉴 목록 없음 또는 조회 실패')

View File

@ -229,36 +229,30 @@
<v-card class="mb-4" elevation="1" v-if="useAI"> <v-card class="mb-4" elevation="1" v-if="useAI">
<v-card-title class="text-h6 py-3">AI 옵션 설정</v-card-title> <v-card-title class="text-h6 py-3">AI 옵션 설정</v-card-title>
<v-card-text> <v-card-text>
<!-- 톤앤매너 --> <!-- 타겟 연령층 - 간단하고 확실한 방법으로 수정 -->
<v-select <v-select
v-model="aiOptions.toneAndManner" v-model="aiOptions.targetAge"
:items="toneOptions" :items="targetAgeOptions"
label="톤앤매너" label="타겟 연령층"
variant="outlined" variant="outlined"
density="compact" density="compact"
class="mb-3" class="mb-3"
/> prepend-inner-icon="mdi-account-group"
>
<!-- 홍보 유형 --> <template #item="{ props, item }">
<v-select <v-list-item
v-model="aiOptions.promotion" v-bind="props"
:items="promotionOptions" :prepend-icon="getAgeIcon(item.value)"
label="홍보 유형" :title="item.title"
variant="outlined" />
density="compact" </template>
class="mb-3" <template #selection="{ item }">
/> <v-chip size="small" color="primary" class="ml-1">
<v-icon start size="small">{{ getAgeIcon(item.value) }}</v-icon>
<!-- 감정 강도 --> {{ item.title }}
<v-select </v-chip>
v-model="aiOptions.emotionIntensity" </template>
:items="emotionOptions" </v-select>
label="감정 강도"
variant="outlined"
density="compact"
class="mb-3"
/>
<!-- 포토 스타일 (포스터인 경우) --> <!-- 포토 스타일 (포스터인 경우) -->
<v-select <v-select
v-if="selectedType === 'poster'" v-if="selectedType === 'poster'"

File diff suppressed because it is too large Load Diff