From 4c5c243acdf1e4ba1d702127a07a647e145278b2 Mon Sep 17 00:00:00 2001 From: aken-you Date: Thu, 9 Oct 2025 15:32:15 +0900 Subject: [PATCH 1/8] =?UTF-8?q?feat:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=EC=97=90=EC=84=9C=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EC=9E=90=20=EC=A0=95=EB=B3=B4=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?UI=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/admin/ui/member-list-table.tsx | 25 ++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/features/admin/ui/member-list-table.tsx b/src/features/admin/ui/member-list-table.tsx index d244a28f..764622ba 100644 --- a/src/features/admin/ui/member-list-table.tsx +++ b/src/features/admin/ui/member-list-table.tsx @@ -102,7 +102,30 @@ export default function MemberListTable() { onChange={setSearchKeyword} /> -
+ +
+
+ {someSelected && ( +
+

+ + {selectedIds.size} + + 명 선택 +

+ +
+ + +
+
+ )} +
+ Date: Thu, 9 Oct 2025 15:42:12 +0900 Subject: [PATCH 2/8] =?UTF-8?q?refactor:=20=EA=B6=8C=ED=95=9C=EA=B3=BC=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=EB=A5=BC=20const=20=ED=8F=B4=EB=8D=94?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/admin/const/member.ts | 12 ++++++++++++ src/features/admin/ui/member-list-table.tsx | 17 +++-------------- 2 files changed, 15 insertions(+), 14 deletions(-) create mode 100644 src/features/admin/const/member.ts diff --git a/src/features/admin/const/member.ts b/src/features/admin/const/member.ts new file mode 100644 index 00000000..4008a566 --- /dev/null +++ b/src/features/admin/const/member.ts @@ -0,0 +1,12 @@ +export const ROLE_MAP = { + ROLE_MEMBER: '일반', + ROLE_MENTOR: '멘토', + ROLE_ADMIN: '관리자', +}; + +export const MEMBER_STATUS_MAP = { + ACTIVE: '활성', + PERM_BAN: '일시정지', + PAUSED: '영구정지', + DORMANT: '휴면', +}; diff --git a/src/features/admin/ui/member-list-table.tsx b/src/features/admin/ui/member-list-table.tsx index 764622ba..d026c86d 100644 --- a/src/features/admin/ui/member-list-table.tsx +++ b/src/features/admin/ui/member-list-table.tsx @@ -9,25 +9,16 @@ import { SingleDropdown } from '@/shared/ui/dropdown'; import Pagination from '@/shared/ui/pagination'; import FilledX from 'public/icons/filled-x.svg'; import SearchIcon from 'public/icons/search.svg'; +import ChangeStatusModal from './chage-status-modal'; import { MemberStatus, RoleId } from '../api/types'; +import { MEMBER_STATUS_MAP, ROLE_MAP } from '../const/member'; import { useGetMemberListQuery } from '../model/use-member-list-query'; -const ROLE_MAP = { - ROLE_MEMBER: '일반', - ROLE_MENTOR: '멘토', - ROLE_ADMIN: '관리자', -}; const ROLE_OPTIONS = Object.entries(ROLE_MAP).map(([key, label]) => ({ value: key, label, })); -const MEMBER_STATUS_MAP = { - ACTIVE: '활성', - PERM_BAN: '일시정지', - PAUSED: '영구정지', - DORMANT: '휴면', -}; const MEMBER_STATUS_OPTIONS = Object.entries(MEMBER_STATUS_MAP).map( ([key, label]) => ({ value: key, @@ -118,9 +109,7 @@ export default function MemberListTable() { - +
)} From dac20b04e67dda85ee667ff9da64a5b2ed24f1da Mon Sep 17 00:00:00 2001 From: aken-you Date: Thu, 9 Oct 2025 15:54:27 +0900 Subject: [PATCH 3/8] =?UTF-8?q?style:=20MemberListFilter=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=EC=9D=98=20=EC=B5=9C=EC=83=81?= =?UTF-8?q?=EC=9C=84=20=EC=9A=94=EC=86=8C=EB=A1=9C=20div=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/admin/ui/member-list-table.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/admin/ui/member-list-table.tsx b/src/features/admin/ui/member-list-table.tsx index d026c86d..9aeb2880 100644 --- a/src/features/admin/ui/member-list-table.tsx +++ b/src/features/admin/ui/member-list-table.tsx @@ -219,7 +219,7 @@ function MemberListFilter({ onSelectMemberStatus: (memberStatus: MemberStatus | null) => void; }) { return ( - <> +
{(roleId || memberStatus) && (
- + ); } From d7b5293571c5959906d5097cc876c8803c047942 Mon Sep 17 00:00:00 2001 From: aken-you Date: Thu, 9 Oct 2025 18:01:16 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EA=B3=84=EC=A0=95=20=EC=83=81=ED=83=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/admin/api/member-list.ts | 19 +++- src/features/admin/api/types.ts | 5 + .../admin/model/use-member-list-query.ts | 59 +++++++++- src/features/admin/ui/chage-status-modal.tsx | 105 ++++++++++++++++++ src/features/admin/ui/member-list-table.tsx | 8 +- 5 files changed, 190 insertions(+), 6 deletions(-) create mode 100644 src/features/admin/ui/chage-status-modal.tsx diff --git a/src/features/admin/api/member-list.ts b/src/features/admin/api/member-list.ts index 064ffedd..f6a16992 100644 --- a/src/features/admin/api/member-list.ts +++ b/src/features/admin/api/member-list.ts @@ -1,6 +1,11 @@ import { axiosInstance } from '@/shared/tanstack-query/axios'; -import { GetMemberListRequest, GetMemberListResponse } from './types'; +import { + ChangeMemberStatusRequest, + GetMemberListRequest, + GetMemberListResponse, +} from './types'; +// 사용자 목록 조회 export const getMemberList = async ({ roleId, memberStatus, @@ -24,3 +29,15 @@ export const getMemberList = async ({ return res.data.content; }; + +// 사용자 계정 상태 변경 +export const changeMemberStatus = async ({ + memberId, + to, +}: ChangeMemberStatusRequest) => { + const res = await axiosInstance.patch( + `/admin/members/${memberId}/status?to=${to}`, + ); + + return res.data; +}; diff --git a/src/features/admin/api/types.ts b/src/features/admin/api/types.ts index 9f4d17a9..46c13a7b 100644 --- a/src/features/admin/api/types.ts +++ b/src/features/admin/api/types.ts @@ -28,3 +28,8 @@ export interface GetMemberListResponse { }; }[]; } + +export interface ChangeMemberStatusRequest { + memberId: number; + to: MemberStatus; +} diff --git a/src/features/admin/model/use-member-list-query.ts b/src/features/admin/model/use-member-list-query.ts index 762ce211..4c835a01 100644 --- a/src/features/admin/model/use-member-list-query.ts +++ b/src/features/admin/model/use-member-list-query.ts @@ -1,7 +1,12 @@ -import { useQuery } from '@tanstack/react-query'; -import { getMemberList } from '../api/member-list'; -import { GetMemberListRequest } from '../api/types'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { changeMemberStatus, getMemberList } from '../api/member-list'; +import { + ChangeMemberStatusRequest, + GetMemberListRequest, + GetMemberListResponse, +} from '../api/types'; +// 사용자 목록 조회 export const useGetMemberListQuery = ({ roleId, memberStatus, @@ -19,3 +24,51 @@ export const useGetMemberListQuery = ({ }), }); }; + +// 사용자 계정 상태 변경 +export const useChangeMemberStatusMutation = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (data: { + members: GetMemberListResponse['content']; + to: ChangeMemberStatusRequest['to']; + }) => { + // 병렬로 선택한 모든 사용자의 상태 변경 요청을 보냄 + // 성공: {status: "fulfilled", value: {memberId, success: true}} + // 실패: {status: "fulfilled", value: {memberId, success: false}} (개별 요청 실패는 catch에서 처리했기 때문에 status가 rejected가 되지 않음) + const results: PromiseSettledResult<{ + memberId: number; + success: boolean; + }>[] = await Promise.allSettled( + data.members.map((member) => + changeMemberStatus({ memberId: member.memberId, to: data.to }) + .then(() => ({ memberId: member.memberId, success: true })) + .catch(() => ({ memberId: member.memberId, success: false })), + ), + ); + + const failedMemberIds = results + .filter( + (result) => result.status === 'fulfilled' && !result.value.success, + ) + .map((result) => + result.status === 'fulfilled' ? result.value.memberId : null, + ) + .filter(Boolean); + + if (failedMemberIds.length > 0) { + const failedMemberNames = data.members + .filter((member) => failedMemberIds.includes(member.memberId)) + .map((member) => member.memberName); + + alert( + `다음 회원들의 상태 변경에 실패했습니다: ${failedMemberNames.join(', ')}`, + ); + } + }, + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: ['memberList'] }); + }, + }); +}; diff --git a/src/features/admin/ui/chage-status-modal.tsx b/src/features/admin/ui/chage-status-modal.tsx new file mode 100644 index 00000000..c78bb755 --- /dev/null +++ b/src/features/admin/ui/chage-status-modal.tsx @@ -0,0 +1,105 @@ +'use client'; + +import { XIcon } from 'lucide-react'; +import { useState } from 'react'; +import Button from '@/shared/ui/button'; +import { Modal } from '@/shared/ui/modal'; +import { RadioGroup, RadioGroupItem } from '@/shared/ui/radio'; +import { GetMemberListResponse, MemberStatus } from '../api/types'; +import { MEMBER_STATUS_MAP } from '../const/member'; +import { useChangeMemberStatusMutation } from '../model/use-member-list-query'; + +const MEMBER_STATUS_OPTIONS = Object.entries(MEMBER_STATUS_MAP).map( + ([key, label]) => ({ + value: key, + label, + }), +); + +interface ChangeStatusModalProps { + members: GetMemberListResponse['content']; +} + +export default function ChangeStatusModal({ members }: ChangeStatusModalProps) { + const [open, setOpen] = useState(false); + + return ( + + + + + + + + + + + 계정 상태 변경 + + setOpen(false)}> + + + + + setOpen(false)} /> + + + + ); +} + +function ChangeStatusForm({ + members, + onClose, +}: { + members: GetMemberListResponse['content']; + onClose: () => void; +}) { + const INIT_STATUS: MemberStatus = 'ACTIVE'; + const [status, setStatus] = useState(INIT_STATUS); + + const { mutate: changeStatus } = useChangeMemberStatusMutation(); + + const handleChangeStatus = () => { + changeStatus( + { members, to: status }, + { + onSuccess: () => { + onClose(); + }, + }, + ); + }; + + return ( + <> + + setStatus(status)} + > + {MEMBER_STATUS_OPTIONS.map(({ value, label }) => ( +
+ + +
+ ))} +
+
+ + + + + + + + + ); +} diff --git a/src/features/admin/ui/member-list-table.tsx b/src/features/admin/ui/member-list-table.tsx index 9aeb2880..d24f6208 100644 --- a/src/features/admin/ui/member-list-table.tsx +++ b/src/features/admin/ui/member-list-table.tsx @@ -41,7 +41,7 @@ export default function MemberListTable() { const memberList = data?.content || []; - const [selectedIds, setSelectedIds] = useState(() => new Set()); + const [selectedIds, setSelectedIds] = useState>(() => new Set()); const headerCheckboxRef = useRef(null); const allSelected = @@ -109,7 +109,11 @@ export default function MemberListTable() { - + + selectedIds.has(member.memberId), + )} + /> )} From 1c9b2907a5517c0674d8a8a8f79e591c5d25ce4d Mon Sep 17 00:00:00 2001 From: aken-you Date: Thu, 9 Oct 2025 18:26:03 +0900 Subject: [PATCH 5/8] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EA=B6=8C=ED=95=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/admin/api/member-list.ts | 13 +++ src/features/admin/api/types.ts | 5 + .../admin/model/use-member-list-query.ts | 55 +++++++++- src/features/admin/ui/change-role-modal.tsx | 103 ++++++++++++++++++ src/features/admin/ui/member-list-table.tsx | 9 +- 5 files changed, 181 insertions(+), 4 deletions(-) create mode 100644 src/features/admin/ui/change-role-modal.tsx diff --git a/src/features/admin/api/member-list.ts b/src/features/admin/api/member-list.ts index f6a16992..6c5251ac 100644 --- a/src/features/admin/api/member-list.ts +++ b/src/features/admin/api/member-list.ts @@ -1,5 +1,6 @@ import { axiosInstance } from '@/shared/tanstack-query/axios'; import { + ChangeMemberRoleRequest, ChangeMemberStatusRequest, GetMemberListRequest, GetMemberListResponse, @@ -41,3 +42,15 @@ export const changeMemberStatus = async ({ return res.data; }; + +// 사용자 권한 변경 +export const changeMemberRole = async ({ + memberId, + roleId, +}: ChangeMemberRoleRequest) => { + const res = await axiosInstance.patch( + `/admin/members/${memberId}/role?role-id=${roleId}`, + ); + + return res.data; +}; diff --git a/src/features/admin/api/types.ts b/src/features/admin/api/types.ts index 46c13a7b..071def3c 100644 --- a/src/features/admin/api/types.ts +++ b/src/features/admin/api/types.ts @@ -33,3 +33,8 @@ export interface ChangeMemberStatusRequest { memberId: number; to: MemberStatus; } + +export interface ChangeMemberRoleRequest { + memberId: number; + roleId: RoleId; +} diff --git a/src/features/admin/model/use-member-list-query.ts b/src/features/admin/model/use-member-list-query.ts index 4c835a01..38422eb3 100644 --- a/src/features/admin/model/use-member-list-query.ts +++ b/src/features/admin/model/use-member-list-query.ts @@ -1,6 +1,11 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { changeMemberStatus, getMemberList } from '../api/member-list'; import { + changeMemberRole, + changeMemberStatus, + getMemberList, +} from '../api/member-list'; +import { + ChangeMemberRoleRequest, ChangeMemberStatusRequest, GetMemberListRequest, GetMemberListResponse, @@ -72,3 +77,51 @@ export const useChangeMemberStatusMutation = () => { }, }); }; + +// 사용자 권한 변경 +export const useChangeMemberRoleMutation = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (data: { + members: GetMemberListResponse['content']; + roleId: ChangeMemberRoleRequest['roleId']; + }) => { + // 병렬로 선택한 모든 사용자의 상태 변경 요청을 보냄 + // 성공: {status: "fulfilled", value: {memberId, success: true}} + // 실패: {status: "fulfilled", value: {memberId, success: false}} (개별 요청 실패는 catch에서 처리했기 때문에 status가 rejected가 되지 않음) + const results: PromiseSettledResult<{ + memberId: number; + success: boolean; + }>[] = await Promise.allSettled( + data.members.map((member) => + changeMemberRole({ memberId: member.memberId, roleId: data.roleId }) + .then(() => ({ memberId: member.memberId, success: true })) + .catch(() => ({ memberId: member.memberId, success: false })), + ), + ); + + const failedMemberIds = results + .filter( + (result) => result.status === 'fulfilled' && !result.value.success, + ) + .map((result) => + result.status === 'fulfilled' ? result.value.memberId : null, + ) + .filter(Boolean); + + if (failedMemberIds.length > 0) { + const failedMemberNames = data.members + .filter((member) => failedMemberIds.includes(member.memberId)) + .map((member) => member.memberName); + + alert( + `다음 회원들의 권한 변경에 실패했습니다: ${failedMemberNames.join(', ')}`, + ); + } + }, + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: ['memberList'] }); + }, + }); +}; diff --git a/src/features/admin/ui/change-role-modal.tsx b/src/features/admin/ui/change-role-modal.tsx new file mode 100644 index 00000000..beebf1fe --- /dev/null +++ b/src/features/admin/ui/change-role-modal.tsx @@ -0,0 +1,103 @@ +'use client'; + +import { XIcon } from 'lucide-react'; +import { useState } from 'react'; +import Button from '@/shared/ui/button'; +import { Modal } from '@/shared/ui/modal'; +import { RadioGroup, RadioGroupItem } from '@/shared/ui/radio'; +import { GetMemberListResponse, RoleId } from '../api/types'; +import { ROLE_MAP } from '../const/member'; +import { useChangeMemberRoleMutation } from '../model/use-member-list-query'; + +const ROLE_OPTIONS = Object.entries(ROLE_MAP).map(([key, label]) => ({ + value: key, + label, +})); + +interface ChangeRoleModalProps { + members: GetMemberListResponse['content']; +} + +export default function ChangeRoleModal({ members }: ChangeRoleModalProps) { + const [open, setOpen] = useState(false); + + return ( + + + + + + + + + + + 권한 변경 + + setOpen(false)}> + + + + + setOpen(false)} /> + + + + ); +} + +function ChangeRoleForm({ + members, + onClose, +}: { + members: GetMemberListResponse['content']; + onClose: () => void; +}) { + const INIT_ROLE: RoleId = 'ROLE_MEMBER'; + const [role, setRole] = useState(INIT_ROLE); + + const { mutate: changeStatus } = useChangeMemberRoleMutation(); + + const handleChangeStatus = () => { + changeStatus( + { members, roleId: role }, + { + onSuccess: () => { + onClose(); + }, + }, + ); + }; + + return ( + <> + + setRole(roleId)} + > + {ROLE_OPTIONS.map(({ value, label }) => ( +
+ + +
+ ))} +
+
+ + + + + + + + + ); +} diff --git a/src/features/admin/ui/member-list-table.tsx b/src/features/admin/ui/member-list-table.tsx index d24f6208..2542e8f7 100644 --- a/src/features/admin/ui/member-list-table.tsx +++ b/src/features/admin/ui/member-list-table.tsx @@ -10,6 +10,7 @@ import Pagination from '@/shared/ui/pagination'; import FilledX from 'public/icons/filled-x.svg'; import SearchIcon from 'public/icons/search.svg'; import ChangeStatusModal from './chage-status-modal'; +import ChangeRoleModal from './change-role-modal'; import { MemberStatus, RoleId } from '../api/types'; import { MEMBER_STATUS_MAP, ROLE_MAP } from '../const/member'; import { useGetMemberListQuery } from '../model/use-member-list-query'; @@ -106,9 +107,11 @@ export default function MemberListTable() {

- + + selectedIds.has(member.memberId), + )} + /> selectedIds.has(member.memberId), From aff7e96c2a2ef82b518071f4ee104cef5d358fa6 Mon Sep 17 00:00:00 2001 From: aken-you Date: Thu, 9 Oct 2025 18:44:03 +0900 Subject: [PATCH 6/8] =?UTF-8?q?refactor:=20=EC=84=A0=ED=83=9D=ED=95=9C=20?= =?UTF-8?q?=ED=96=89=20=EB=B0=B0=EA=B2=BD=EC=83=89=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/admin/ui/member-list-table.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/features/admin/ui/member-list-table.tsx b/src/features/admin/ui/member-list-table.tsx index 2542e8f7..37fe3c5e 100644 --- a/src/features/admin/ui/member-list-table.tsx +++ b/src/features/admin/ui/member-list-table.tsx @@ -163,11 +163,15 @@ export default function MemberListTable() { {memberList.map((user, idx) => ( Date: Thu, 9 Oct 2025 18:45:51 +0900 Subject: [PATCH 7/8] =?UTF-8?q?refactor:=20=EA=B6=8C=ED=95=9C=EA=B3=BC=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EB=B0=B0=EC=97=B4=20=EC=83=81=EC=88=98?= =?UTF-8?q?=EB=A1=9C=20=EA=B4=80=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/admin/const/member.ts | 12 ++++++++++++ src/features/admin/ui/chage-status-modal.tsx | 9 +-------- src/features/admin/ui/change-role-modal.tsx | 7 +------ src/features/admin/ui/member-list-table.tsx | 19 ++++++------------- 4 files changed, 20 insertions(+), 27 deletions(-) diff --git a/src/features/admin/const/member.ts b/src/features/admin/const/member.ts index 4008a566..9c269162 100644 --- a/src/features/admin/const/member.ts +++ b/src/features/admin/const/member.ts @@ -4,9 +4,21 @@ export const ROLE_MAP = { ROLE_ADMIN: '관리자', }; +export const ROLE_OPTIONS = Object.entries(ROLE_MAP).map(([key, label]) => ({ + value: key, + label, +})); + export const MEMBER_STATUS_MAP = { ACTIVE: '활성', PERM_BAN: '일시정지', PAUSED: '영구정지', DORMANT: '휴면', }; + +export const MEMBER_STATUS_OPTIONS = Object.entries(MEMBER_STATUS_MAP).map( + ([key, label]) => ({ + value: key, + label, + }), +); diff --git a/src/features/admin/ui/chage-status-modal.tsx b/src/features/admin/ui/chage-status-modal.tsx index c78bb755..18d2cb13 100644 --- a/src/features/admin/ui/chage-status-modal.tsx +++ b/src/features/admin/ui/chage-status-modal.tsx @@ -6,16 +6,9 @@ import Button from '@/shared/ui/button'; import { Modal } from '@/shared/ui/modal'; import { RadioGroup, RadioGroupItem } from '@/shared/ui/radio'; import { GetMemberListResponse, MemberStatus } from '../api/types'; -import { MEMBER_STATUS_MAP } from '../const/member'; +import { MEMBER_STATUS_OPTIONS } from '../const/member'; import { useChangeMemberStatusMutation } from '../model/use-member-list-query'; -const MEMBER_STATUS_OPTIONS = Object.entries(MEMBER_STATUS_MAP).map( - ([key, label]) => ({ - value: key, - label, - }), -); - interface ChangeStatusModalProps { members: GetMemberListResponse['content']; } diff --git a/src/features/admin/ui/change-role-modal.tsx b/src/features/admin/ui/change-role-modal.tsx index beebf1fe..b7751d8a 100644 --- a/src/features/admin/ui/change-role-modal.tsx +++ b/src/features/admin/ui/change-role-modal.tsx @@ -6,14 +6,9 @@ import Button from '@/shared/ui/button'; import { Modal } from '@/shared/ui/modal'; import { RadioGroup, RadioGroupItem } from '@/shared/ui/radio'; import { GetMemberListResponse, RoleId } from '../api/types'; -import { ROLE_MAP } from '../const/member'; +import { ROLE_OPTIONS } from '../const/member'; import { useChangeMemberRoleMutation } from '../model/use-member-list-query'; -const ROLE_OPTIONS = Object.entries(ROLE_MAP).map(([key, label]) => ({ - value: key, - label, -})); - interface ChangeRoleModalProps { members: GetMemberListResponse['content']; } diff --git a/src/features/admin/ui/member-list-table.tsx b/src/features/admin/ui/member-list-table.tsx index 37fe3c5e..d410c360 100644 --- a/src/features/admin/ui/member-list-table.tsx +++ b/src/features/admin/ui/member-list-table.tsx @@ -12,21 +12,14 @@ import SearchIcon from 'public/icons/search.svg'; import ChangeStatusModal from './chage-status-modal'; import ChangeRoleModal from './change-role-modal'; import { MemberStatus, RoleId } from '../api/types'; -import { MEMBER_STATUS_MAP, ROLE_MAP } from '../const/member'; +import { + MEMBER_STATUS_MAP, + MEMBER_STATUS_OPTIONS, + ROLE_MAP, + ROLE_OPTIONS, +} from '../const/member'; import { useGetMemberListQuery } from '../model/use-member-list-query'; -const ROLE_OPTIONS = Object.entries(ROLE_MAP).map(([key, label]) => ({ - value: key, - label, -})); - -const MEMBER_STATUS_OPTIONS = Object.entries(MEMBER_STATUS_MAP).map( - ([key, label]) => ({ - value: key, - label, - }), -); - export default function MemberListTable() { const [roleId, setRoleId] = useState(null); const [memberStatus, setMemberStatus] = useState(null); From f45182776d555eb0e240171ae4482c999a47aef5 Mon Sep 17 00:00:00 2001 From: aken-you Date: Thu, 9 Oct 2025 18:49:40 +0900 Subject: [PATCH 8/8] =?UTF-8?q?refactor:=20=EB=A9=98=ED=86=A0=EC=9D=BC=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20=EC=98=86=EC=97=90=20check=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=EC=9C=BC=EB=A1=9C=20=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icons/seal-check.svg | 10 ++++++++++ src/features/admin/ui/member-list-table.tsx | 4 +++- 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 public/icons/seal-check.svg diff --git a/public/icons/seal-check.svg b/public/icons/seal-check.svg new file mode 100644 index 00000000..57512572 --- /dev/null +++ b/public/icons/seal-check.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/features/admin/ui/member-list-table.tsx b/src/features/admin/ui/member-list-table.tsx index d410c360..f6b135c3 100644 --- a/src/features/admin/ui/member-list-table.tsx +++ b/src/features/admin/ui/member-list-table.tsx @@ -8,6 +8,7 @@ import Checkbox from '@/shared/ui/checkbox'; import { SingleDropdown } from '@/shared/ui/dropdown'; import Pagination from '@/shared/ui/pagination'; import FilledX from 'public/icons/filled-x.svg'; +import SealCheckIcon from 'public/icons/seal-check.svg'; import SearchIcon from 'public/icons/search.svg'; import ChangeStatusModal from './chage-status-modal'; import ChangeRoleModal from './change-role-modal'; @@ -182,7 +183,8 @@ export default function MemberListTable() { {formatYYYYMMDD(user.loginMostRecentlyAt)} - + + {user.role.roleId === 'ROLE_MENTOR' && } {ROLE_MAP[user.role.roleId]}