storeManagement contentmanagement edit

This commit is contained in:
unknown 2025-06-12 15:48:27 +09:00
parent 7315bac5a3
commit e0a23ed6d2
3 changed files with 1361 additions and 611 deletions

View File

@ -19,53 +19,308 @@ export const useContentStore = defineStore('content', () => {
dateRange: 'all',
})
// 임시 데이터 (실제로는 API에서 가져옴)
// 풍부한 더미 데이터
const sampleContents = [
{
id: 1,
title: '떡볶이 신메뉴 출시 이벤트',
title: '🌶️ 떡볶이 신메뉴 출시 이벤트',
type: 'sns',
platform: 'instagram',
content: '🌶️ 떡볶이 신메뉴 출시! 치즈가 듬뿍 들어간 치즈떡볶이가 새로 나왔어요!',
hashtags: ['떡볶이', '신메뉴', '치즈떡볶이', '맛집'],
platform: 'INSTAGRAM',
content: '🌶️ 떡볶이 신메뉴 출시! 치즈가 듬뿍 들어간 치즈떡볶이가 새로 나왔어요! 매콤한 떡볶이와 고소한 치즈의 완벽한 조화✨\n\n#떡볶이 #신메뉴 #치즈떡볶이 #맛집 #분식 #매콤달콤',
hashtags: ['떡볶이', '신메뉴', '치즈떡볶이', '맛집', '분식', '매콤달콤'],
images: ['/images/menu-placeholder.png'],
status: 'published',
status: 'PUBLISHED',
views: 1240,
likes: 85,
comments: 12,
createdAt: new Date('2024-01-15T10:30:00'),
publishedAt: new Date('2024-01-15T14:00:00'),
createdAt: '2024-01-15T10:30:00',
publishedAt: '2024-01-15T14:00:00',
},
{
id: 2,
title: '매장 소개 포스터',
title: '📸 우리 매장 소개 포스터',
type: 'poster',
platform: 'blog',
content: '우리 매장을 소개하는 포스터입니다.',
hashtags: ['매장소개', '분식집', '맛집'],
platform: 'POSTER',
content: '따뜻한 분위기의 우리 매장을 소개합니다. 정성스럽게 만든 음식으로 여러분을 맞이하겠습니다.',
hashtags: ['매장소개', '분식집', '맛집', '정성'],
images: ['/images/store-placeholder.png'],
status: 'draft',
status: 'DRAFT',
views: 0,
likes: 0,
comments: 0,
createdAt: new Date('2024-01-14T16:20:00'),
createdAt: '2024-01-14T16:20:00',
publishedAt: null,
},
{
id: 3,
title: '할인 이벤트 안내',
title: '🎉 특별 할인 이벤트 안내',
type: 'sns',
platform: 'instagram',
content: '🎉 특별 할인 이벤트! 오늘 하루만 모든 메뉴 20% 할인!',
hashtags: ['할인', '이벤트', '특가', '분식'],
images: ['/images/ai-character.png'],
status: 'scheduled',
platform: 'INSTAGRAM',
content: '🎉 오늘 하루만 특별 할인! 모든 메뉴 20% 할인 이벤트 진행중🔥\n\n놓치면 후회할 기회! 지금 바로 방문하세요!\n\n⏰ 할인 시간: 오후 6시까지\n📍 위치: 서울 강남구 테헤란로 123',
hashtags: ['할인', '이벤트', '특가', '분식', '강남맛집'],
images: ['/images/event-poster.png'],
status: 'PUBLISHED',
views: 892,
likes: 45,
comments: 8,
createdAt: '2024-01-13T09:15:00',
publishedAt: '2024-01-13T12:00:00',
},
{
id: 4,
title: '🍜 김치찌개 맛집 블로그 포스팅',
type: 'blog',
platform: 'NAVER_BLOG',
content: '추운 겨울, 몸을 따뜻하게 해줄 김치찌개 한 그릇은 어떠세요? 우리 매장의 시그니처 메뉴인 김치찌개를 소개합니다.\n\n✅ 3년 묵은 김치 사용\n✅ 국내산 돼지고기\n✅ 진한 국물맛\n✅ 푸짐한 양\n\n한 번 맛보시면 계속 생각나는 그런 맛입니다!',
hashtags: ['김치찌개', '맛집', '추천메뉴', '겨울음식', '따뜻한국물'],
images: ['/images/kimchi-jjigae.jpg'],
status: 'PUBLISHED',
views: 2156,
likes: 123,
comments: 15,
createdAt: '2024-01-12T14:45:00',
publishedAt: '2024-01-12T16:00:00',
},
{
id: 5,
title: '🥢 순대국밥 홍보 포스터',
type: 'poster',
platform: 'POSTER',
content: '진짜 맛있는 순대국밥! 24시간 끓인 진한 국물과 신선한 순대로 만든 특별한 한 그릇',
hashtags: ['순대국밥', '24시간', '진한국물', '신선한순대'],
images: ['/images/sundae-soup.jpg'],
status: 'PUBLISHED',
views: 567,
likes: 34,
comments: 3,
createdAt: '2024-01-11T11:20:00',
publishedAt: '2024-01-11T15:30:00',
},
{
id: 6,
title: '🌈 컬러풀 떡볶이 인스타 스토리',
type: 'sns',
platform: 'INSTAGRAM',
content: '무지개 떡볶이 만들어봤어요! 🌈 천연 색소로 만든 건강한 컬러 떡볶이✨ 맛도 색깔만큼 다양해요!\n\n빨강: 매운맛 🔥\n노랑: 치즈맛 🧀\n초록: 깻잎맛 🌿\n보라: 자색고구마맛 🍠',
hashtags: ['컬러떡볶이', '무지개떡볶이', '천연색소', '건강한간식', '예쁜음식'],
images: ['/images/colorful-tteokbokki.jpg'],
status: 'DRAFT',
views: 0,
likes: 0,
comments: 0,
createdAt: new Date('2024-01-13T09:15:00'),
scheduledAt: new Date('2024-01-16T12:00:00'),
createdAt: '2024-01-10T13:30:00',
publishedAt: null,
},
{
id: 7,
title: '🍖 불고기 김밥 신메뉴 소개',
type: 'blog',
platform: 'NAVER_BLOG',
content: '달콤짭짤한 불고기와 신선한 채소가 들어간 불고기 김밥이 새로 출시되었습니다!\n\n🥩 국내산 소고기 불고기\n🥬 신선한 상추와 당근\n🍚 고슬고슬한 밥\n🥒 아삭한 단무지\n\n한 입 베어물면 입 안 가득 퍼지는 불고기의 달콤함과 채소의 아삭함이 조화롭게 어우러집니다.',
hashtags: ['불고기김밥', '신메뉴', '국내산소고기', '신선한채소', '달콤짭짤'],
images: ['/images/bulgogi-kimbap.jpg'],
status: 'PUBLISHED',
views: 1834,
likes: 76,
comments: 9,
createdAt: '2024-01-09T16:15:00',
publishedAt: '2024-01-09T18:00:00',
},
{
id: 8,
title: '🎊 개업 1주년 기념 이벤트',
type: 'poster',
platform: 'POSTER',
content: '감사합니다! 개업 1주년을 맞아 특별 이벤트를 준비했습니다. 고객님들의 사랑에 보답하는 마음으로!',
hashtags: ['1주년', '감사이벤트', '특별할인', '기념일'],
images: ['/images/anniversary-event.jpg'],
status: 'DRAFT',
views: 0,
likes: 0,
comments: 0,
createdAt: '2024-01-08T10:00:00',
publishedAt: null,
},
{
id: 9,
title: '🍲 라면 끓이기 꿀팁 공유',
type: 'sns',
platform: 'NAVER_BLOG',
content: '집에서도 맛있는 라면 끓이는 법! 우리 매장 셰프가 알려주는 특별한 비법을 공개합니다.\n\n1⃣ 물이 끓기 시작할 때 면 투입\n2⃣ 스프는 면이 익은 후 넣기\n3⃣ 계란은 마지막 1분에 추가\n4⃣ 파는 불을 끄기 직전에!\n\n이 방법으로 끓이면 훨씬 더 맛있어요! 한번 시도해보세요 😊',
hashtags: ['라면끓이기', '꿀팁', '요리비법', '맛있는라면', '셰프추천'],
images: ['/images/ramen-cooking.jpg'],
status: 'PUBLISHED',
views: 3245,
likes: 189,
comments: 27,
createdAt: '2024-01-07T12:30:00',
publishedAt: '2024-01-07T14:00:00',
},
{
id: 10,
title: '🥟 수제 만두 제작 과정',
type: 'sns',
platform: 'INSTAGRAM',
content: '손으로 직접 빚는 우리 매장의 수제 만두! 👐\n\n매일 아침 일찍 와서 정성스럽게 만듭니다 🥟\n신선한 재료만 사용해요!\n\n📹 만두 빚는 과정 영상도 준비했어요\n👀 스토리에서 확인하세요!',
hashtags: ['수제만두', '정성', '신선한재료', '매일제작', '장인정신'],
images: ['/images/handmade-mandu.jpg'],
status: 'PUBLISHED',
views: 987,
likes: 67,
comments: 5,
createdAt: '2024-01-06T08:45:00',
publishedAt: '2024-01-06T10:00:00',
},
{
id: 11,
title: '❄️ 겨울 한정 메뉴 출시',
type: 'poster',
platform: 'POSTER',
content: '추운 겨울을 따뜻하게! 겨울 한정 메뉴들이 출시되었습니다. 몸과 마음을 따뜻하게 해드릴게요.',
hashtags: ['겨울한정', '따뜻한음식', '한정메뉴', '계절메뉴'],
images: ['/images/winter-menu.jpg'],
status: 'DRAFT',
views: 0,
likes: 0,
comments: 0,
createdAt: '2024-01-05T15:20:00',
publishedAt: null,
},
{
id: 12,
title: '🍜 우동 맛집 후기 모음',
type: 'blog',
platform: 'NAVER_BLOG',
content: '고객님들이 남겨주신 우동 후기를 모아봤습니다! 정말 감사한 마음입니다 💕\n\n"국물이 정말 진해요!"\n"면발이 쫄깃쫄깃해서 좋아요"\n"야채가 신선하고 양도 많아요"\n"사장님이 친절하세요"\n\n앞으로도 더 맛있는 우동으로 보답하겠습니다!',
hashtags: ['우동', '고객후기', '진한국물', '쫄깃한면', '신선한야채'],
images: ['/images/udon-reviews.jpg'],
status: 'PUBLISHED',
views: 1456,
likes: 89,
comments: 12,
createdAt: '2024-01-04T14:10:00',
publishedAt: '2024-01-04T16:30:00',
},
{
id: 13,
title: '🎯 주말 특가 이벤트',
type: 'sns',
platform: 'INSTAGRAM',
content: '주말 특가 이벤트! 🎯\n\n토요일, 일요일 이틀간만!\n모든 음료 50% 할인! 🥤\n\n✅ 콜라, 사이다\n✅ 주스류\n✅ 전통차\n✅ 커피\n\n주말에 가족, 친구들과 함께 오세요! 👨‍👩‍👧‍👦',
hashtags: ['주말특가', '음료할인', '50%할인', '가족외식', '친구모임'],
images: ['/images/weekend-drink-sale.jpg'],
status: 'PUBLISHED',
views: 1123,
likes: 78,
comments: 14,
createdAt: '2024-01-03T11:00:00',
publishedAt: '2024-01-03T13:00:00',
},
{
id: 14,
title: '🏆 맛집 인증서 획득!',
type: 'poster',
platform: 'POSTER',
content: '드디어! 지역 맛집 인증서를 받았습니다! 고객님들의 사랑 덕분입니다. 감사합니다!',
hashtags: ['맛집인증', '지역맛집', '인증서', '고객감사'],
images: ['/images/restaurant-certificate.jpg'],
status: 'PUBLISHED',
views: 756,
likes: 124,
comments: 18,
createdAt: '2024-01-02T09:30:00',
publishedAt: '2024-01-02T11:00:00',
},
{
id: 15,
title: '🌟 2024년 신년 인사',
type: 'sns',
platform: 'NAVER_BLOG',
content: '2024년 새해가 밝았습니다! ✨\n\n지난 한 해 동안 저희 매장을 사랑해주신 모든 고객님들께 진심으로 감사드립니다.\n\n새해에도 더욱 맛있는 음식과 따뜻한 서비스로 보답하겠습니다.\n\n🎊 새해 복 많이 받으세요! 🎊\n\n올해도 많은 관심과 사랑 부탁드립니다!',
hashtags: ['신년인사', '새해복', '고객감사', '2024년', '따뜻한서비스'],
images: ['/images/new-year-greeting.jpg'],
status: 'PUBLISHED',
views: 2341,
likes: 156,
comments: 23,
createdAt: '2024-01-01T00:00:00',
publishedAt: '2024-01-01T09:00:00',
},
{
id: 16,
title: '🍕 피자떡볶이 실험중!',
type: 'sns',
platform: 'INSTAGRAM',
content: '새로운 메뉴 개발중! 🍕+🌶️\n\n피자떡볶이가 과연 맛있을까요?\nR&D 중인 신메뉴를 살짝 공개! 👀\n\n토마토 소스 베이스에\n모짜렐라 치즈 토핑\n바질과 오레가노 향신료까지!\n\n출시 전 맛보기 이벤트도 계획중이에요!',
hashtags: ['피자떡볶이', '신메뉴개발', 'R&D', '실험적메뉴', '맛보기이벤트'],
images: ['/images/pizza-tteokbokki.jpg'],
status: 'DRAFT',
views: 0,
likes: 0,
comments: 0,
createdAt: '2023-12-30T16:45:00',
publishedAt: null,
},
{
id: 17,
title: '🎄 크리스마스 특별 메뉴',
type: 'poster',
platform: 'POSTER',
content: '크리스마스를 맛있게! 특별한 날을 위한 특별한 메뉴들을 준비했습니다.',
hashtags: ['크리스마스', '특별메뉴', '연말', '특별한날'],
images: ['/images/christmas-menu.jpg'],
status: 'PUBLISHED',
views: 1678,
likes: 93,
comments: 7,
createdAt: '2023-12-20T10:15:00',
publishedAt: '2023-12-20T12:00:00',
},
{
id: 18,
title: '🔥 매운맛 도전 이벤트',
type: 'sns',
platform: 'INSTAGRAM',
content: '매운맛 도전자 모집! 🔥🔥🔥\n\n👹 지옥 떡볶이 도전!\n🌶 매운맛 단계별 도전\n🏅 완주시 상품 증정\n📹 도전 영상 촬영 가능\n\n용기있는 분들의 도전을 기다립니다!\n(단, 매운 것 못 드시는 분은 조심하세요 😅)',
hashtags: ['매운맛도전', '지옥떡볶이', '도전이벤트', '상품증정', '용기있는자'],
images: ['/images/spicy-challenge.jpg'],
status: 'PUBLISHED',
views: 2567,
likes: 234,
comments: 45,
createdAt: '2023-12-15T14:20:00',
publishedAt: '2023-12-15T15:00:00',
},
{
id: 19,
title: '☕ 겨울 음료 메뉴 소개',
type: 'blog',
platform: 'NAVER_BLOG',
content: '추운 겨울, 따뜻한 음료 한 잔은 어떠세요? ☕\n\n우리 매장의 겨울 음료 메뉴를 소개합니다:\n\n🍫 진한 핫초콜릿\n🍯 꿀유자차\n🌿 생강차\n☕ 아메리카노\n🥛 따뜻한 우유\n\n모든 음료는 직접 우린 차와 신선한 재료로 만듭니다!\n겨울 추위를 이겨내는 따뜻함을 선사해드릴게요.',
hashtags: ['겨울음료', '따뜻한차', '핫초콜릿', '꿀유자차', '생강차'],
images: ['/images/winter-drinks.jpg'],
status: 'PUBLISHED',
views: 1234,
likes: 67,
comments: 8,
createdAt: '2023-12-10T13:30:00',
publishedAt: '2023-12-10T15:00:00',
},
{
id: 20,
title: '🍱 도시락 배달 서비스 시작',
type: 'poster',
platform: 'POSTER',
content: '이제 도시락 배달도 가능합니다! 회사나 집에서 편리하게 주문하세요.',
hashtags: ['도시락배달', '배달서비스', '회사도시락', '편리한주문'],
images: ['/images/lunchbox-delivery.jpg'],
status: 'DRAFT',
views: 0,
likes: 0,
comments: 0,
createdAt: '2023-12-05T11:45:00',
publishedAt: null,
}
]
// Getters
@ -90,15 +345,15 @@ export const useContentStore = defineStore('content', () => {
})
const publishedContents = computed(() =>
contents.value.filter((content) => content.status === 'published')
contents.value.filter((content) => content.status === 'PUBLISHED')
)
const draftContents = computed(() =>
contents.value.filter((content) => content.status === 'draft')
contents.value.filter((content) => content.status === 'DRAFT')
)
const scheduledContents = computed(() =>
contents.value.filter((content) => content.status === 'scheduled')
contents.value.filter((content) => content.status === 'SCHEDULED')
)
const recentContents = computed(() => {
@ -114,17 +369,18 @@ export const useContentStore = defineStore('content', () => {
)
// Actions
const loadContents = async () => {
const fetchContents = async () => {
try {
isLoading.value = true
// API 호출 시뮬레이션
await new Promise((resolve) => setTimeout(resolve, 1000))
// 임시로 샘플 데이터 사용
// 더미 데이터 로드
contents.value = [...sampleContents]
console.log('콘텐츠 목록 로드 완료:', contents.value.length)
return contents.value
} catch (error) {
console.error('콘텐츠 로드 실패:', error)
throw error
@ -133,6 +389,10 @@ export const useContentStore = defineStore('content', () => {
}
}
const loadContents = async () => {
return await fetchContents()
}
const getContentById = (id) => {
return contents.value.find((content) => content.id === parseInt(id))
}
@ -141,7 +401,7 @@ export const useContentStore = defineStore('content', () => {
const newContent = {
...content,
id: Date.now(), // 임시 ID
createdAt: new Date(),
createdAt: new Date().toISOString(),
views: 0,
likes: 0,
comments: 0,
@ -150,27 +410,66 @@ export const useContentStore = defineStore('content', () => {
return newContent
}
const updateContent = (contentId, updatedData) => {
const index = contents.value.findIndex((content) => content.id === contentId)
if (index !== -1) {
contents.value[index] = {
...contents.value[index],
...updatedData,
updatedAt: new Date(),
const updateContent = async (contentId, updatedData) => {
try {
const index = contents.value.findIndex((content) => content.id === contentId)
if (index !== -1) {
contents.value[index] = {
...contents.value[index],
...updatedData,
updatedAt: new Date().toISOString(),
}
// API 호출 시뮬레이션
await new Promise((resolve) => setTimeout(resolve, 500))
return contents.value[index]
}
return contents.value[index]
throw new Error('콘텐츠를 찾을 수 없습니다.')
} catch (error) {
console.error('콘텐츠 수정 실패:', error)
throw error
}
return null
}
const deleteContent = (contentId) => {
const index = contents.value.findIndex((content) => content.id === contentId)
if (index !== -1) {
const deletedContent = contents.value[index]
contents.value.splice(index, 1)
return deletedContent
const deleteContent = async (contentId) => {
try {
const index = contents.value.findIndex((content) => content.id === contentId)
if (index !== -1) {
const deletedContent = contents.value[index]
contents.value.splice(index, 1)
// API 호출 시뮬레이션
await new Promise((resolve) => setTimeout(resolve, 300))
return deletedContent
}
throw new Error('콘텐츠를 찾을 수 없습니다.')
} catch (error) {
console.error('콘텐츠 삭제 실패:', error)
throw error
}
}
const deleteMultipleContents = async (contentIds) => {
try {
// API 호출 시뮬레이션
await new Promise((resolve) => setTimeout(resolve, 500))
const deletedContents = []
contentIds.forEach(id => {
const index = contents.value.findIndex((content) => content.id === id)
if (index !== -1) {
deletedContents.push(contents.value[index])
contents.value.splice(index, 1)
}
})
return deletedContents
} catch (error) {
console.error('콘텐츠 일괄 삭제 실패:', error)
throw error
}
return null
}
const publishContent = async (contentId) => {
@ -181,8 +480,8 @@ export const useContentStore = defineStore('content', () => {
await new Promise((resolve) => setTimeout(resolve, 1000))
const content = updateContent(contentId, {
status: 'published',
publishedAt: new Date(),
status: 'PUBLISHED',
publishedAt: new Date().toISOString(),
})
console.log('콘텐츠 발행 완료:', content?.title)
@ -203,8 +502,8 @@ export const useContentStore = defineStore('content', () => {
await new Promise((resolve) => setTimeout(resolve, 500))
const content = updateContent(contentId, {
status: 'scheduled',
scheduledAt: new Date(scheduledTime),
status: 'SCHEDULED',
scheduledAt: new Date(scheduledTime).toISOString(),
})
console.log('콘텐츠 예약 완료:', content?.title)
@ -231,7 +530,7 @@ export const useContentStore = defineStore('content', () => {
content: `AI가 생성한 ${options.type} 콘텐츠입니다. ${options.description || ''}`,
hashtags: options.hashtags || [],
images: options.images || [],
status: 'draft',
status: 'DRAFT',
}
const newContent = addContent(generatedContent)
@ -285,10 +584,12 @@ export const useContentStore = defineStore('content', () => {
totalLikes,
// Actions
loadContents,
fetchContents,
getContentById,
addContent,
updateContent,
deleteContent,
deleteMultipleContents,
publishContent,
scheduleContent,
generateContent,
@ -297,4 +598,4 @@ export const useContentStore = defineStore('content', () => {
setCurrentContent,
clearCurrentContent,
}
})
})

File diff suppressed because it is too large Load Diff

View File

@ -466,10 +466,532 @@
>
{{ snackbar.message }}
</v-snackbar>
<!-- 매장 등록/수정 다이얼로그 -->
<v-dialog
v-model="showCreateDialog"
max-width="800"
persistent
scrollable
>
<v-card>
<v-card-title class="pa-4">
<span class="text-h6">{{ editMode ? '매장 정보 수정' : '매장 정보 등록' }}</span>
<v-spacer />
</v-card-title>
<v-divider />
<v-card-text class="pa-6" style="max-height: 500px;">
<v-form ref="storeForm" v-model="formValid">
<v-row>
<!-- 매장 이미지 -->
<v-col cols="12">
<h4 class="text-subtitle-1 font-weight-bold mb-3">매장 이미지</h4>
<div class="text-center mb-4">
<v-avatar size="120" class="mb-3">
<v-img
:src="formData.imageUrl || '/images/store-placeholder.png'"
alt="매장 이미지"
/>
</v-avatar>
<br>
<v-btn
color="primary"
variant="outlined"
prepend-icon="mdi-camera"
@click="selectImage"
>
이미지 선택
</v-btn>
<input
ref="imageInput"
type="file"
accept="image/*"
style="display: none;"
@change="handleImageUpload"
>
</div>
</v-col>
<!-- 기본 정보 -->
<v-col cols="12" sm="6">
<v-text-field
v-model="formData.storeName"
label="매장명 *"
variant="outlined"
:rules="[v => !!v || '매장명을 입력해주세요']"
required
/>
</v-col>
<v-col cols="12" sm="6">
<v-select
v-model="formData.businessType"
label="업종 *"
variant="outlined"
:items="businessTypes"
:rules="[v => !!v || '업종을 선택해주세요']"
required
/>
</v-col>
<v-col cols="12" sm="6">
<v-text-field
v-model="formData.ownerName"
label="사업자명 *"
variant="outlined"
:rules="[v => !!v || '사업자명을 입력해주세요']"
required
/>
</v-col>
<v-col cols="12" sm="6">
<v-text-field
v-model="formData.businessNumber"
label="사업자등록번호 *"
variant="outlined"
:rules="businessNumberRules"
required
/>
</v-col>
<v-col cols="12">
<v-text-field
v-model="formData.address"
label="주소 *"
variant="outlined"
:rules="[v => !!v || '주소를 입력해주세요']"
required
/>
</v-col>
<v-col cols="12" sm="6">
<v-text-field
v-model="formData.phoneNumber"
label="연락처 *"
variant="outlined"
:rules="phoneRules"
required
/>
</v-col>
<v-col cols="12" sm="6">
<v-text-field
v-model.number="formData.seatCount"
label="좌석 수"
variant="outlined"
type="number"
min="0"
/>
</v-col>
<!-- SNS 정보 -->
<div class="form-section">
<h3 class="text-h6 font-weight-bold mb-4">SNS 계정 정보</h3>
<v-row>
<!-- 인스타그램 -->
<v-col cols="12" md="6">
<div class="d-flex align-center mb-2">
<v-icon color="purple" class="mr-2">mdi-instagram</v-icon>
<span class="text-subtitle-2 font-weight-medium">인스타그램</span>
</div>
<div class="d-flex gap-2 align-center">
<v-text-field
v-model="formData.instagramUrl"
placeholder="@계정명 또는 URL 입력"
variant="outlined"
density="comfortable"
prepend-inner-icon="mdi-at"
hide-details="auto"
class="flex-grow-1"
/>
<v-btn
color="purple"
size="small"
variant="tonal"
:loading="snsCheckLoading.instagram"
@click="checkSnsConnection('instagram')"
>
연동 확인
</v-btn>
</div>
</v-col>
<!-- 네이버 블로그 -->
<v-col cols="12" md="6">
<div class="d-flex align-center mb-2">
<v-icon color="green" class="mr-2">mdi-blogger</v-icon>
<span class="text-subtitle-2 font-weight-medium">네이버 블로그</span>
</div>
<div class="d-flex gap-2 align-center">
<v-text-field
v-model="formData.blogUrl"
placeholder="blog.naver.com/계정명"
variant="outlined"
density="comfortable"
prepend-inner-icon="mdi-web"
hide-details="auto"
class="flex-grow-1"
/>
<v-btn
color="green"
size="small"
variant="tonal"
:loading="snsCheckLoading.naver_blog"
@click="checkSnsConnection('naver_blog')"
>
연동 확인
</v-btn>
</div>
</v-col>
</v-row>
</div>
<!-- 운영 정보 -->
<v-col cols="12">
<h4 class="text-subtitle-1 font-weight-bold mb-3">운영 정보</h4>
</v-col>
<v-col cols="12" sm="6">
<v-text-field
v-model="formData.openTime"
label="오픈 시간"
variant="outlined"
type="time"
/>
</v-col>
<v-col cols="12" sm="6">
<v-text-field
v-model="formData.closeTime"
label="마감 시간"
variant="outlined"
type="time"
/>
</v-col>
<v-col cols="12">
<v-select
v-model="formData.holidays"
label="휴무일"
variant="outlined"
:items="daysOfWeek"
multiple
chips
/>
</v-col>
<v-col cols="12" sm="6">
<v-switch
v-model="formData.deliveryAvailable"
label="배달 서비스"
color="primary"
/>
</v-col>
<v-col cols="12" sm="6">
<v-switch
v-model="formData.takeoutAvailable"
label="포장 서비스"
color="primary"
/>
</v-col>
</v-row>
</v-form>
</v-card-text>
<v-divider />
<v-card-actions class="pa-4">
<v-spacer />
<v-btn
variant="text"
@click="closeDialog"
>
취소
</v-btn>
<v-btn
color="primary"
:loading="saving"
:disabled="!formValid"
@click="saveStoreInfo"
>
{{ editMode ? '수정하기' : '등록하기' }}
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- 메뉴 등록/수정 다이얼로그 -->
<v-dialog
v-model="showMenuDialog"
max-width="600"
persistent
scrollable
>
<v-card>
<v-card-title class="pa-4">
<span class="text-h6">{{ editMenuMode ? '메뉴 수정' : '메뉴 등록' }}</span>
<v-spacer />
<v-btn
icon
@click="closeMenuDialog"
>
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<v-divider />
<v-card-text class="pa-6" style="max-height: 500px;">
<v-form ref="menuForm" v-model="menuFormValid">
<!-- 메뉴 이미지 -->
<div class="text-center mb-6">
<v-img
:src="menuFormData.imageUrl || '/images/menu-placeholder.png'"
:alt="menuFormData.menuName"
max-width="200"
max-height="150"
class="mx-auto mb-3 rounded"
/>
<v-btn
color="primary"
variant="outlined"
prepend-icon="mdi-camera"
@click="selectMenuImage"
>
이미지 선택
</v-btn>
<input
ref="menuImageInput"
type="file"
accept="image/*"
style="display: none;"
@change="handleMenuImageUpload"
>
</div>
<v-row>
<v-col cols="12" sm="8">
<v-text-field
v-model="menuFormData.menuName"
label="메뉴명 *"
variant="outlined"
:rules="[v => !!v || '메뉴명을 입력해주세요']"
required
/>
</v-col>
<v-col cols="12" sm="4">
<v-text-field
v-model.number="menuFormData.price"
label="가격 *"
variant="outlined"
type="number"
prefix="₩"
:rules="priceRules"
required
/>
</v-col>
<v-col cols="12">
<v-combobox
v-model="menuFormData.category"
label="카테고리 *"
variant="outlined"
:items="menuCategories"
:rules="[v => !!v || '카테고리를 선택해주세요']"
required
/>
</v-col>
<v-col cols="12">
<v-textarea
v-model="menuFormData.description"
label="메뉴 설명"
variant="outlined"
rows="3"
placeholder="메뉴에 대한 자세한 설명을 입력해주세요"
/>
</v-col>
<v-col cols="12" sm="6">
<div class="d-flex flex-column gap-2 mt-4">
<v-switch
v-model="menuFormData.available"
label="판매 중"
color="success"
/>
<v-switch
v-model="menuFormData.recommended"
label="추천 메뉴"
color="warning"
/>
</div>
</v-col>
</v-row>
</v-form>
</v-card-text>
<v-divider />
<v-card-actions class="pa-4">
<v-spacer />
<v-btn
variant="text"
@click="closeMenuDialog"
>
취소
</v-btn>
<v-btn
color="primary"
:loading="savingMenu"
:disabled="!menuFormValid"
@click="saveMenu"
>
{{ editMenuMode ? '수정하기' : '등록하기' }}
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- 메뉴 삭제 확인 다이얼로그 -->
<v-dialog v-model="showDeleteMenuDialog" max-width="400">
<v-card>
<v-card-title class="text-h6">메뉴 삭제</v-card-title>
<v-card-text>
<p>정말로 <strong>{{ deleteMenuTarget?.menuName }}</strong> 메뉴를 삭제하시겠습니까?</p>
<v-alert type="warning" variant="tonal" class="mt-3">
삭제된 메뉴는 복구할 없습니다.
</v-alert>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
color="grey"
variant="text"
@click="showDeleteMenuDialog = false"
>
취소
</v-btn>
<v-btn
color="error"
:loading="deletingMenu"
@click="deleteMenu"
>
삭제
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- 메뉴 상세 다이얼로그 -->
<v-dialog v-model="showMenuDetailDialog" max-width="500">
<v-card class="menu-detail-card">
<v-card-title class="pa-4">
<span class="text-h6">메뉴 상세 정보</span>
<v-spacer />
<v-btn
icon
@click="showMenuDetailDialog = false"
>
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<v-divider />
<v-card-text class="pa-4" v-if="selectedMenuDetail">
<!-- 메뉴 이미지 -->
<div class="text-center mb-4">
<v-img
:src="selectedMenuDetail.imageUrl || '/images/menu-placeholder.png'"
:alt="selectedMenuDetail.menuName"
max-width="300"
max-height="200"
class="mx-auto rounded"
/>
</div>
<!-- 메뉴 기본 정보 -->
<div class="menu-detail-info mb-4">
<h3 class="text-h6 font-weight-bold mb-2">{{ selectedMenuDetail.menuName }}</h3>
<p class="text-body-1 mb-2">
{{ selectedMenuDetail.description || '설명이 없습니다.' }}
</p>
</div>
<!-- 메뉴 정보 카드들 -->
<v-row>
<v-col cols="6">
<v-card variant="tonal" color="primary" class="text-center pa-4">
<v-icon size="32" class="mb-2">mdi-food</v-icon>
<h4 class="text-subtitle-1 font-weight-bold">카테고리</h4>
<p class="text-body-2">{{ selectedMenuDetail.category }}</p>
</v-card>
</v-col>
<v-col cols="6">
<v-card variant="tonal" color="success" class="text-center pa-4">
<v-icon size="32" class="mb-2">mdi-currency-krw</v-icon>
<h4 class="text-subtitle-1 font-weight-bold">가격</h4>
<p class="text-body-2">{{ formatCurrency(selectedMenuDetail.price) }}</p>
</v-card>
</v-col>
</v-row>
</v-card-text>
<v-divider />
<v-card-actions class="pa-4 justify-end">
<v-btn
variant="text"
@click="showMenuDetailDialog = false"
>
닫기
</v-btn>
<v-btn
color="primary"
@click="editFromDetail"
>
수정하기
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!-- SNS 연동 확인 결과 다이얼로그 -->
<v-dialog v-model="showSnsResultDialog" max-width="400">
<v-card>
<v-card-title class="pa-4">
<v-icon :color="snsConnectionResult.success ? 'success' : 'error'" class="mr-2">
{{ snsConnectionResult.success ? 'mdi-check-circle' : 'mdi-alert-circle' }}
</v-icon>
SNS 연동 확인
</v-card-title>
<v-card-text class="pa-4">
<p>{{ snsConnectionResult.message }}</p>
</v-card-text>
<v-card-actions class="pa-4">
<v-spacer />
<v-btn
color="primary"
@click="showSnsResultDialog = false"
>
확인
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-container>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { useStoreStore } from '@/stores/store'
import { useStoreStore } from '@/store/index'
/**
* AI 마케팅 서비스 - 매장 관리 페이지