관리자 멤버 상세와 추가 화면 구현

This commit is contained in:
2026-05-13 11:18:06 +09:00
parent b4f3fdb77d
commit fb0dadb7b9
16 changed files with 778 additions and 36 deletions

View File

@@ -0,0 +1,35 @@
<script setup>
definePageMeta({
layout: 'admin'
})
const route = useRoute()
const memberId = computed(() => String(route.params.id || ''))
const { data: member, error } = await useFetch(() => `/admin/api/members/${memberId.value}`, {
default: () => null
})
/**
* 저장된 회원 정보로 화면 상태를 갱신한다.
* @param {Object} savedMember - 저장된 회원
* @returns {void}
*/
const handleMemberSaved = (savedMember) => {
member.value = savedMember
}
</script>
<template>
<AdminMemberForm
v-if="member"
:member="member"
mode="edit"
@saved="handleMemberSaved"
/>
<section v-else class="admin-member-detail bg-paper p-6">
<div class="rounded-xl border border-line bg-white px-5 py-8 text-sm text-muted">
{{ error?.data?.message || '회원 정보를 불러올 수 없습니다.' }}
</div>
</section>
</template>

View File

@@ -29,6 +29,19 @@ const memberCountLabel = computed(() => {
return `${count}`
})
/**
* 회원 상세 화면으로 이동한다.
* @param {Object} member - 회원 정보
* @returns {Promise<void>} 이동 처리
*/
const navigateToMember = async (member) => {
if (!member?.id) {
return
}
await navigateTo(`/admin/members/${member.id}`)
}
/**
* 회원 이니셜을 반환한다.
* @param {Object} member - 회원 정보
@@ -100,9 +113,12 @@ const formatRelativeTime = (value) => {
<template>
<section class="admin-members bg-paper p-6">
<div class="admin-members__header flex flex-col gap-5 border-b border-line pb-6 xl:flex-row xl:items-center xl:justify-between">
<div class="admin-members__header flex flex-col gap-5 pb-6 xl:flex-row xl:items-center xl:justify-between">
<div>
<h1 class="admin-members__title text-3xl font-semibold tracking-[-0.01em] text-[#15171a]">
<p class="admin-members__eyebrow text-xs font-semibold uppercase text-muted">
Members
</p>
<h1 class="admin-members__title mt-2 text-3xl font-semibold tracking-[-0.01em] text-[#15171a]">
멤버
</h1>
</div>
@@ -120,12 +136,12 @@ const formatRelativeTime = (value) => {
aria-label="멤버 검색"
>
</label>
<button
class="admin-members__new h-11 rounded-md bg-[#15171a] px-4 text-sm font-semibold text-white transition hover:bg-[#2b2f35] focus-visible:outline focus-visible:ring-2 focus-visible:ring-[#8e9cac] focus-visible:ring-offset-2"
type="button"
<NuxtLink
class="admin-members__new inline-flex h-11 items-center rounded-md bg-[#15171a] px-4 text-sm font-semibold text-white transition hover:bg-[#2b2f35] focus-visible:outline focus-visible:ring-2 focus-visible:ring-[#8e9cac] focus-visible:ring-offset-2"
to="/admin/members/new"
>
멤버 추가
</button>
</NuxtLink>
</div>
</div>
@@ -141,7 +157,14 @@ const formatRelativeTime = (value) => {
</tr>
</thead>
<tbody class="admin-members__table-body divide-y divide-line/70 bg-paper">
<tr v-for="member in filteredMembers" :key="member.id" class="admin-members__row align-middle transition-colors hover:bg-[#f7f8fa]">
<tr
v-for="member in filteredMembers"
:key="member.id"
class="admin-members__row cursor-pointer align-middle transition-colors hover:bg-[#f7f8fa] focus-within:bg-[#f7f8fa]"
tabindex="0"
@click="navigateToMember(member)"
@keydown.enter.prevent="navigateToMember(member)"
>
<td class="admin-members__cell py-5 pr-5">
<div class="admin-members__profile flex min-w-0 items-center gap-3">
<img

View File

@@ -0,0 +1,22 @@
<script setup>
definePageMeta({
layout: 'admin'
})
/**
* 새 회원 저장 후 상세 화면으로 이동한다.
* @param {Object} member - 저장된 회원
* @returns {Promise<void>} 이동 처리
*/
const handleMemberSaved = async (member) => {
if (!member?.id) {
return
}
await navigateTo(`/admin/members/${member.id}`)
}
</script>
<template>
<AdminMemberForm mode="new" @saved="handleMemberSaved" />
</template>