Files
tier-maker/frontend/src/composables/useAdminCustomItems.js

210 lines
6.8 KiB
JavaScript

import { nextTick } from 'vue'
export function useAdminCustomItems({
api,
toast,
customItems,
customItemPage,
customItemLimit,
customItemPageCount,
customItemQuery,
customItemFilter,
customItemModalOpen,
customItemDeleteModalOpen,
customItemModalHistoryActive,
modalTargetCustomItem,
customItemModalDraftLabel,
customItemModalLabelSaving,
customItemModalTargetTemplateId,
templates,
selectedTemplateId,
refreshCustomItems,
loadTemplate,
setTab,
selectAdminTemplate,
resetMessages,
success,
error,
}) {
function submitCustomItemSearch() {
customItemPage.value = 1
refreshCustomItems()
}
function changeCustomItemFilter(filter) {
customItemFilter.value = filter
customItemPage.value = 1
refreshCustomItems()
}
function changeCustomItemLimit(limit) {
customItemLimit.value = limit
customItemPage.value = 1
refreshCustomItems()
}
function moveCustomItemPage(direction) {
const nextPage = customItemPage.value + direction
if (nextPage < 1 || nextPage > customItemPageCount.value) return
customItemPage.value = nextPage
refreshCustomItems()
}
function pushCustomItemModalHistoryState() {
if (typeof window === 'undefined') return
window.history.pushState({ ...(window.history.state || {}), adminCustomItemModal: true }, '', window.location.href)
customItemModalHistoryActive.value = true
}
function openCustomItemModal(item) {
modalTargetCustomItem.value = item || null
customItemModalDraftLabel.value = item?.label || ''
customItemModalTargetTemplateId.value = ''
customItemModalOpen.value = true
pushCustomItemModalHistoryState()
}
function closeCustomItemModal({ fromPopState = false } = {}) {
customItemModalOpen.value = false
customItemDeleteModalOpen.value = false
modalTargetCustomItem.value = null
customItemModalDraftLabel.value = ''
customItemModalLabelSaving.value = false
customItemModalTargetTemplateId.value = ''
if (fromPopState) {
customItemModalHistoryActive.value = false
return
}
if (customItemModalHistoryActive.value && typeof window !== 'undefined') {
customItemModalHistoryActive.value = false
window.history.back()
}
}
function openCustomItemDeleteModal(item) {
if (!item) return
if (item.sourceType === 'user' && (item.usageCount > 0 || item.linkedTemplates.length > 0)) {
error.value = '사용 중이거나 템플릿에 연결된 사용자 업로드 이미지는 먼저 참조를 정리해야 삭제할 수 있어요.'
return
}
modalTargetCustomItem.value = item
customItemDeleteModalOpen.value = true
}
function closeCustomItemDeleteModal() {
customItemDeleteModalOpen.value = false
}
function jumpToTemplateAdmin(templateId) {
if (!templateId) return
closeCustomItemModal()
setTab('template-admin')
nextTick(() => {
selectAdminTemplate(templateId)
})
}
async function removeCustomItem(item = modalTargetCustomItem.value) {
resetMessages()
if (!item) return
if (item.sourceType === 'user' && (item.usageCount > 0 || item.linkedTemplates.length > 0)) {
error.value = '사용 중이거나 템플릿에 연결된 사용자 업로드 이미지는 먼저 참조를 정리해야 삭제할 수 있어요.'
return
}
try {
await api.deleteAdminCustomItem(item.id)
closeCustomItemDeleteModal()
closeCustomItemModal()
await refreshCustomItems()
success.value =
item.sourceType === 'template'
? '선택한 템플릿 아이템을 제거했어요.'
: item.sourceType === 'asset'
? '선택한 이미지 자산을 삭제했어요.'
: '사용자 업로드 이미지를 삭제했어요.'
} catch (e) {
error.value =
item.sourceType === 'template'
? '템플릿 아이템 제거에 실패했어요.'
: item.sourceType === 'asset'
? '이미지 자산 삭제에 실패했어요.'
: '사용자 업로드 이미지 삭제에 실패했어요.'
}
}
async function removeUnusedCustomItems() {
resetMessages()
const ok = window.confirm('현재 검색 조건에 맞는 미사용 커스텀 이미지를 모두 삭제할까요?')
if (!ok) return
try {
const data = await api.deleteAdminUnusedCustomItems({ q: customItemQuery.value })
await refreshCustomItems()
success.value = `${data.deletedCount || 0}개의 미사용 사용자 업로드 이미지를 삭제했어요.`
} catch (e) {
error.value = '미사용 커스텀 이미지 일괄 삭제에 실패했어요.'
}
}
async function saveCustomItemModalLabel() {
const item = modalTargetCustomItem.value
const nextLabel = customItemModalDraftLabel.value.trim().slice(0, 60)
if (!item || !nextLabel || nextLabel === item.label || customItemModalLabelSaving.value) return
try {
customItemModalLabelSaving.value = true
const data = await api.updateAdminCustomItemLabel(item.id, { label: nextLabel, sourceType: item.sourceType })
item.label = data.item?.label || nextLabel
customItemModalDraftLabel.value = item.label
customItems.value = customItems.value.map((entry) => (entry.id === item.id ? { ...entry, label: item.label } : entry))
toast.success('아이템 이름을 변경했어요.')
} catch (e) {
error.value = '아이템 이름 변경에 실패했어요.'
} finally {
customItemModalLabelSaving.value = false
}
}
async function promoteCustomItem(item) {
resetMessages()
if (!customItemModalTargetTemplateId.value) {
error.value = '추가할 템플릿을 먼저 선택해주세요.'
return
}
try {
item.isPromoting = true
await api.promoteAdminTemplateItem(item.id, { topicId: customItemModalTargetTemplateId.value })
const targetTemplateName =
templates.value.find((template) => template.id === customItemModalTargetTemplateId.value)?.name || customItemModalTargetTemplateId.value
if (selectedTemplateId.value === customItemModalTargetTemplateId.value) await loadTemplate()
closeCustomItemModal()
success.value = `"${item.label}" 이미지를 ${targetTemplateName} 템플릿으로 추가했어요.`
} catch (e) {
error.value = '선택한 이미지를 템플릿으로 추가하지 못했어요.'
} finally {
item.isPromoting = false
}
}
return {
submitCustomItemSearch,
changeCustomItemFilter,
changeCustomItemLimit,
moveCustomItemPage,
pushCustomItemModalHistoryState,
openCustomItemModal,
closeCustomItemModal,
openCustomItemDeleteModal,
closeCustomItemDeleteModal,
jumpToTemplateAdmin,
removeCustomItem,
removeUnusedCustomItems,
saveCustomItemModalLabel,
promoteCustomItem,
}
}