diff --git a/components/admin/AdminMemberForm.vue b/components/admin/AdminMemberForm.vue index 371c6f9..dbe6ed3 100644 --- a/components/admin/AdminMemberForm.vue +++ b/components/admin/AdminMemberForm.vue @@ -12,6 +12,13 @@ const props = defineProps({ const emit = defineEmits(['saved', 'deleted']) +const { data: adminSession } = await useFetch('/admin/api/auth/me', { + default: () => ({ + userId: '', + roleCode: '' + }) +}) + const isNewMember = computed(() => props.mode === 'new') const saveMessage = ref('') const saveError = ref('') @@ -87,6 +94,57 @@ const normalizedLabels = computed(() => [...new Set( )]) const currentRoleLabel = computed(() => roleOptions.find((option) => option.value === form.roleCode)?.label || '멤버') +const currentAdminRoleCode = computed(() => adminSession.value?.roleCode || '') +const isCurrentAdminPrivileged = computed(() => ['owner', 'admin'].includes(currentAdminRoleCode.value)) +const isEditingSelf = computed(() => Boolean(props.member?.id && adminSession.value?.userId) + && String(props.member.id) === String(adminSession.value.userId)) +const isTargetPrivilegedRole = computed(() => ['owner', 'admin'].includes(props.member?.roleCode || form.roleCode)) +const shouldRenderRoleAsText = computed(() => isNewMember.value || !isCurrentAdminPrivileged.value) +const canEditRoleSelect = computed(() => { + if (shouldRenderRoleAsText.value || isSaving.value) { + return false + } + + if (currentAdminRoleCode.value === 'owner') { + return !isEditingSelf.value + } + + if (currentAdminRoleCode.value === 'admin') { + return !isTargetPrivilegedRole.value + } + + return false +}) +const availableRoleOptions = computed(() => { + if (!canEditRoleSelect.value) { + return roleOptions + } + + if (currentAdminRoleCode.value === 'admin') { + return roleOptions.filter((option) => ['vip', 'member'].includes(option.value)) + } + + return roleOptions +}) +const roleHelpText = computed(() => { + if (shouldRenderRoleAsText.value) { + return '멤버와 VIP는 관리자 권한이 없어 등급을 변경할 수 없습니다.' + } + + if (isEditingSelf.value && currentAdminRoleCode.value === 'owner') { + return '소유자는 본인 권한을 직접 낮출 수 없습니다.' + } + + if (currentAdminRoleCode.value === 'admin' && isTargetPrivilegedRole.value) { + return '관리자는 소유자 또는 다른 관리자의 등급을 변경할 수 없습니다.' + } + + if (currentAdminRoleCode.value === 'admin') { + return '관리자는 멤버와 VIP 등급만 변경할 수 있습니다.' + } + + return 'VIP 이상 등급은 멤버십 게시물을 볼 수 있습니다.' +}) /** * 회원 저장 요청 본문을 문자열로 직렬화한다. @@ -383,7 +441,7 @@ const saveMember = async () => { try { const payload = getMemberPayload() - if (!isNewMember.value && form.roleCode !== props.member?.roleCode) { + if (!isNewMember.value && canEditRoleSelect.value && form.roleCode !== props.member?.roleCode) { await $fetch(`/admin/api/members/${props.member.id}/role`, { method: 'PUT', body: { @@ -558,17 +616,28 @@ watch(() => props.member, () => { diff --git a/components/admin/AdminPageForm.vue b/components/admin/AdminPageForm.vue index 51ff148..0065fe3 100644 --- a/components/admin/AdminPageForm.vue +++ b/components/admin/AdminPageForm.vue @@ -40,6 +40,23 @@ const htmlCursorRange = reactive({ end: 0 }) +const defaultHtmlDocument = ` + +
+ + +