구형 에디터 제거와 토스트 공통화

This commit is contained in:
2026-07-23 16:23:01 +09:00
parent b61a6a2295
commit 151349edc2
13 changed files with 55 additions and 2697 deletions

View File

@@ -9,8 +9,7 @@ const saving = ref(false)
const deleting = ref(false)
const errorMessage = ref('')
const pageForm = ref(null)
const toast = ref(null)
let toastTimer = null
const { toast, showToast } = useAdminToast()
const { data: page } = await useFetch(() => `/admin/api/pages/${id.value}`)
@@ -23,20 +22,6 @@ if (!page.value) {
const publicPageUrl = computed(() => page.value?.slug ? `/pages/${page.value.slug}` : '')
/**
* 저장 상태 토스트 표시
* @param {'success'|'error'|'info'} type - 토스트 타입
* @param {string} message - 표시 메시지
* @returns {void}
*/
const showToast = (type, message) => {
window.clearTimeout(toastTimer)
toast.value = { type, message }
toastTimer = window.setTimeout(() => {
toast.value = null
}, 3200)
}
/**
* 이전 화면에서 전달한 토스트 표시
* @returns {void}
@@ -111,10 +96,6 @@ const deletePage = async () => {
}
onMounted(showStoredToast)
onBeforeUnmount(() => {
window.clearTimeout(toastTimer)
})
</script>
<template>
@@ -135,7 +116,7 @@ onBeforeUnmount(() => {
/>
<div
v-if="toast"
class="admin-page-edit__toast fixed right-5 top-5 z-50 rounded border px-4 py-3 text-sm font-semibold shadow-lg"
class="admin-page-edit__toast fixed right-5 top-5 z-[100] max-w-[min(24rem,calc(100vw-2.5rem))] rounded border px-4 py-3 text-sm font-semibold shadow-lg"
:class="{
'border-green-200 bg-green-50 text-green-800': toast.type === 'success',
'border-red-200 bg-red-50 text-red-800': toast.type === 'error',

View File

@@ -7,22 +7,7 @@ const saving = ref(false)
const errorMessage = ref('')
const pageForm = ref(null)
const createdPageId = ref('')
const toast = ref(null)
let toastTimer = null
/**
* 저장 상태 토스트 표시
* @param {'success'|'error'|'info'} type - 토스트 타입
* @param {string} message - 표시 메시지
* @returns {void}
*/
const showToast = (type, message) => {
window.clearTimeout(toastTimer)
toast.value = { type, message }
toastTimer = window.setTimeout(() => {
toast.value = null
}, 3200)
}
const { toast, showToast } = useAdminToast()
/**
* 새 고정 페이지 저장
@@ -54,10 +39,6 @@ const savePage = async (payload) => {
saving.value = false
}
}
onBeforeUnmount(() => {
window.clearTimeout(toastTimer)
})
</script>
<template>
@@ -68,7 +49,7 @@ onBeforeUnmount(() => {
<AdminPageForm ref="pageForm" :saving="saving" @submit="savePage" />
<div
v-if="toast"
class="admin-page-editor__toast fixed right-5 top-5 z-50 rounded border px-4 py-3 text-sm font-semibold shadow-lg"
class="admin-page-editor__toast fixed right-5 top-5 z-[100] max-w-[min(24rem,calc(100vw-2.5rem))] rounded border px-4 py-3 text-sm font-semibold shadow-lg"
:class="{
'border-green-200 bg-green-50 text-green-800': toast.type === 'success',
'border-red-200 bg-red-50 text-red-800': toast.type === 'error',

View File

@@ -10,8 +10,7 @@ const autoSaving = ref(false)
const deleting = ref(false)
const errorMessage = ref('')
const postForm = ref(null)
const toast = ref(null)
let toastTimer = null
const { toast, showToast, clearToast } = useAdminToast()
const { data: post } = await useFetch(() => `/admin/api/posts/${id.value}`)
@@ -32,20 +31,6 @@ const isPublicPost = (value) => value?.status === 'published'
const publicPostUrl = computed(() => post.value?.slug ? `/post/${post.value.slug}` : '')
/**
* 저장 상태 토스트 표시
* @param {'success'|'error'|'info'} type - 토스트 타입
* @param {string} message - 표시 메시지
* @returns {void}
*/
const showToast = (type, message) => {
window.clearTimeout(toastTimer)
toast.value = { type, message }
toastTimer = window.setTimeout(() => {
toast.value = null
}, 3200)
}
/**
* 저장 오류가 슬러그 중복인지 확인한다.
* @param {unknown} error - 저장 오류
@@ -109,8 +94,7 @@ const savePost = async (payload) => {
showToast('success', '변경 내용이 저장되었습니다.')
} catch (error) {
if (isDuplicateSlugError(error)) {
window.clearTimeout(toastTimer)
toast.value = null
clearToast()
errorMessage.value = ''
postForm.value?.markSlugError('이미 사용 중인 슬러그입니다.')
return
@@ -146,8 +130,7 @@ const autosaveDraftPost = async (payload) => {
const msg = error?.data?.message || error?.message || '자동 저장에 실패했습니다.'
postForm.value?.markAutosaveFailed(payload)
if (isDuplicateSlugError(error)) {
window.clearTimeout(toastTimer)
toast.value = null
clearToast()
errorMessage.value = ''
postForm.value?.markSlugError('이미 사용 중인 슬러그입니다.')
return false
@@ -196,10 +179,6 @@ const deletePost = async () => {
}
onMounted(showStoredToast)
onBeforeUnmount(() => {
window.clearTimeout(toastTimer)
})
</script>
<template>
@@ -223,7 +202,7 @@ onBeforeUnmount(() => {
/>
<div
v-if="toast"
class="admin-post-edit__toast fixed right-5 top-5 z-50 rounded border px-4 py-3 text-sm font-semibold shadow-lg"
class="admin-post-edit__toast fixed right-5 top-5 z-[100] max-w-[min(24rem,calc(100vw-2.5rem))] rounded border px-4 py-3 text-sm font-semibold shadow-lg"
:class="{
'border-green-200 bg-green-50 text-green-800': toast.type === 'success',
'border-red-200 bg-red-50 text-red-800': toast.type === 'error',

View File

@@ -7,8 +7,7 @@ const saving = ref(false)
const autoSaving = ref(false)
const errorMessage = ref('')
const postForm = ref(null)
const toast = ref(null)
let toastTimer = null
const { toast, showToast, clearToast } = useAdminToast()
/**
* 비교용 게시물 입력값을 직렬화한다.
@@ -58,20 +57,6 @@ const syncLatestDraftAfterCreate = async (postId, firstPayload) => {
return savedPayload
}
/**
* 저장 상태 토스트 표시
* @param {'success'|'error'|'info'} type - 토스트 타입
* @param {string} message - 표시 메시지
* @returns {void}
*/
const showToast = (type, message) => {
window.clearTimeout(toastTimer)
toast.value = { type, message }
toastTimer = window.setTimeout(() => {
toast.value = null
}, 3200)
}
/**
* 저장 오류가 슬러그 중복인지 확인한다.
* @param {unknown} error - 저장 오류
@@ -120,8 +105,7 @@ const savePost = async (payload) => {
await navigateTo(`/admin/posts/${post.id}`)
} catch (error) {
if (isDuplicateSlugError(error)) {
window.clearTimeout(toastTimer)
toast.value = null
clearToast()
errorMessage.value = ''
postForm.value?.markSlugError('이미 사용 중인 슬러그입니다.')
return
@@ -166,8 +150,7 @@ const createDraftAutosave = async (payload, options = {}) => {
const msg = error?.data?.message || error?.message || '자동 저장에 실패했습니다.'
postForm.value?.markAutosaveFailed(payload)
if (isDuplicateSlugError(error)) {
window.clearTimeout(toastTimer)
toast.value = null
clearToast()
errorMessage.value = ''
postForm.value?.markSlugError('이미 사용 중인 슬러그입니다.')
return false
@@ -189,9 +172,6 @@ onBeforeRouteLeave(async () => {
return createDraftAutosave(payload, { skipNavigate: true })
})
onBeforeUnmount(() => {
window.clearTimeout(toastTimer)
})
</script>
<template>
@@ -225,7 +205,7 @@ onBeforeUnmount(() => {
</div>
<div
v-if="toast"
class="admin-post-editor__toast fixed right-5 top-5 z-50 rounded border px-4 py-3 text-sm font-semibold shadow-lg"
class="admin-post-editor__toast fixed right-5 top-5 z-[100] max-w-[min(24rem,calc(100vw-2.5rem))] rounded border px-4 py-3 text-sm font-semibold shadow-lg"
:class="{
'border-green-200 bg-green-50 text-green-800': toast.type === 'success',
'border-red-200 bg-red-50 text-red-800': toast.type === 'error',

View File

@@ -80,7 +80,7 @@ const postExportChunkSize = ref(500)
const postExportMaxFileSizeMb = ref(500)
const postImportExportPanel = ref('')
const errorMessage = ref('')
const toast = ref(null)
const { toast, showToast } = useAdminToast()
const logoInputRef = ref(null)
const homeCoverInputRef = ref(null)
const homeCoverDarkInputRef = ref(null)
@@ -182,7 +182,6 @@ const adsSnapshot = reactive({
adPostInArticleCode: '',
adPostBottomCode: ''
})
let toastTimer = null
let scrollSpyFrame = null
let postExportRefreshTimer = null
@@ -906,20 +905,6 @@ const closeSettings = async () => {
await navigateTo('/admin')
}
/**
* 저장 상태 토스트 표시
* @param {'success'|'error'|'info'} type - 토스트 타입
* @param {string} message - 표시 메시지
* @returns {void}
*/
const showToast = (type, message) => {
window.clearTimeout(toastTimer)
toast.value = { type, message }
toastTimer = window.setTimeout(() => {
toast.value = null
}, 3200)
}
/**
* 게시물 export 작업을 요청한다.
* @returns {Promise<void>}
@@ -2178,7 +2163,6 @@ onMounted(() => {
})
onBeforeUnmount(() => {
window.clearTimeout(toastTimer)
window.clearInterval(postExportRefreshTimer)
if (scrollSpyFrame) {
cancelAnimationFrame(scrollSpyFrame)
@@ -4106,7 +4090,7 @@ onBeforeUnmount(() => {
<div
v-if="toast"
class="admin-settings-screen__toast fixed right-5 top-24 z-[60] rounded-md border px-4 py-3 text-sm font-semibold shadow-lg md:top-28"
class="admin-settings-screen__toast fixed right-5 top-24 z-[100] max-w-[min(24rem,calc(100vw-2.5rem))] rounded-md border px-4 py-3 text-sm font-semibold shadow-lg md:top-28"
:class="{
'border-green-200 bg-green-50 text-green-800': toast.type === 'success',
'border-red-200 bg-red-50 text-red-800': toast.type === 'error',

View File

@@ -9,11 +9,10 @@ const savingOrder = ref(false)
const promotingTagId = ref('')
const demotingTagId = ref('')
const deletingGeneralTagId = ref('')
const toast = ref(null)
const { toast, showToast } = useAdminToast()
const TAG_TOAST_STORAGE_KEY = 'SORI_ADMIN_TAG_TOAST'
const { openMenuId, closeMenu } = useAdminRowMenu()
let toastTimer = null
const generalTagQuery = ref('')
const {
displayValue: generalTagQueryInput,
@@ -103,20 +102,6 @@ const isManagedOrderDirty = computed(() => {
return currentIds.some((id, index) => id !== base[index])
})
/**
* 피드백 토스트 표시
* @param {'success'|'error'|'info'} type - 토스트 타입
* @param {string} message - 표시 메시지
* @returns {void}
*/
const showToast = (type, message) => {
window.clearTimeout(toastTimer)
toast.value = { type, message }
toastTimer = window.setTimeout(() => {
toast.value = null
}, 3200)
}
onMounted(() => {
const storedToast = sessionStorage.getItem(TAG_TOAST_STORAGE_KEY)
@@ -361,10 +346,6 @@ const deleteGeneralTag = async (tag) => {
}
}
onBeforeUnmount(() => {
window.clearTimeout(toastTimer)
})
</script>
<template>
@@ -573,7 +554,7 @@ onBeforeUnmount(() => {
<div
v-if="toast"
class="admin-tags__toast fixed right-5 top-5 z-50 rounded border px-4 py-3 text-sm font-semibold shadow-lg"
class="admin-tags__toast fixed right-5 top-5 z-[100] max-w-[min(24rem,calc(100vw-2.5rem))] rounded border px-4 py-3 text-sm font-semibold shadow-lg"
:class="{
'border-green-200 bg-green-50 text-green-800': toast.type === 'success',
'border-red-200 bg-red-50 text-red-800': toast.type === 'error',