Compare commits
2 Commits
5d8189ccf1
...
4baaeb3c1d
| Author | SHA1 | Date | |
|---|---|---|---|
| 4baaeb3c1d | |||
| 1947d15497 |
@@ -67,21 +67,6 @@ export const getAdminMenuListByParentSeq = async (parentSeq: number, pageNum: nu
|
||||
return { content: [], pageNum: 1, pageSize: 0, totalContent: 0, totalPage: 0, isFirstPage: true, isLastPage: true };
|
||||
};
|
||||
|
||||
// parentSeq로 어드민 메뉴 목록 조회 (페이징 없이 전체 목록)
|
||||
export const getAdminMenuListByParentSeqAll = async (parentSeq: number): Promise<AdminMenu[]> => {
|
||||
const response = await axios.get<AdminMenuResponse>(`/admin/menu/listByParentSeq/${parentSeq}`);
|
||||
const resultData = response.data.resultData;
|
||||
// 페이징 형태의 응답인 경우 content 배열 반환
|
||||
if (resultData && resultData.content && Array.isArray(resultData.content)) {
|
||||
return resultData.content;
|
||||
}
|
||||
// 배열 형태의 응답인 경우 그대로 반환
|
||||
if (Array.isArray(resultData)) {
|
||||
return resultData;
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
// adminMenuSeq로 어드민 메뉴 조회
|
||||
export const getAdminMenu = async (adminMenuSeq: number): Promise<AdminMenu> => {
|
||||
const response = await axios.get<AdminMenuResponse>(`/admin/menu/${adminMenuSeq}`);
|
||||
|
||||
@@ -160,6 +160,7 @@ const AdminMenuManagement: React.FC = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [deleteModalVisible, setDeleteModalVisible] = useState(false);
|
||||
const [hasChildMenus, setHasChildMenus] = useState(false);
|
||||
const [iconPickerVisible, setIconPickerVisible] = useState(false);
|
||||
const [isEditMode, setIsEditMode] = useState(false);
|
||||
const [selectedMenu, setSelectedMenu] = useState<AdminMenu | null>(null);
|
||||
@@ -340,9 +341,22 @@ const AdminMenuManagement: React.FC = () => {
|
||||
};
|
||||
|
||||
// 메뉴 삭제 모달 열기
|
||||
const handleDeleteClick = (menu: AdminMenu) => {
|
||||
setSelectedMenu(menu);
|
||||
setDeleteModalVisible(true);
|
||||
const handleDeleteClick = async (menu: AdminMenu) => {
|
||||
if (!menu.adminMenuSeq) return;
|
||||
|
||||
try {
|
||||
// 하위 메뉴 존재 여부 확인
|
||||
const childMenusResponse = await getAdminMenuListByParentSeq(menu.adminMenuSeq);
|
||||
setHasChildMenus(childMenusResponse.totalContent > 0);
|
||||
setSelectedMenu(menu);
|
||||
setDeleteModalVisible(true);
|
||||
} catch (error) {
|
||||
console.error('하위 메뉴 확인 실패:', error);
|
||||
// 에러 발생 시에도 모달은 열되, 삭제 불가로 처리
|
||||
setHasChildMenus(true);
|
||||
setSelectedMenu(menu);
|
||||
setDeleteModalVisible(true);
|
||||
}
|
||||
};
|
||||
|
||||
// 입력 필드 변경 처리
|
||||
@@ -415,63 +429,63 @@ const AdminMenuManagement: React.FC = () => {
|
||||
<CCard className="mb-4">
|
||||
<CCardHeader>
|
||||
<strong>어드민 메뉴 관리</strong>
|
||||
<CButton
|
||||
color="primary"
|
||||
size="sm"
|
||||
className="float-end"
|
||||
onClick={handleAddClick}
|
||||
>
|
||||
<CIcon icon={cilPlus} className="me-1" />
|
||||
메뉴 추가
|
||||
</CButton>
|
||||
</CCardHeader>
|
||||
<CCardBody>
|
||||
{/* 브레드크럼 네비게이션 */}
|
||||
<nav aria-label="breadcrumb" className="mb-3">
|
||||
<ol className="breadcrumb mb-0">
|
||||
<li className={`breadcrumb-item ${breadcrumb.length === 0 ? 'active' : ''}`}>
|
||||
{breadcrumb.length === 0 ? (
|
||||
<span><CIcon icon={cilHome} className="me-1" />최상위 메뉴</span>
|
||||
) : (
|
||||
<a
|
||||
href="#"
|
||||
onClick={(e) => { e.preventDefault(); handleBreadcrumbClick(-1); }}
|
||||
style={{ textDecoration: 'none' }}
|
||||
>
|
||||
<CIcon icon={cilHome} className="me-1" />최상위 메뉴
|
||||
</a>
|
||||
)}
|
||||
</li>
|
||||
{breadcrumb.map((item, index) => (
|
||||
<li
|
||||
key={item.adminMenuSeq}
|
||||
className={`breadcrumb-item ${index === breadcrumb.length - 1 ? 'active' : ''}`}
|
||||
>
|
||||
{index === breadcrumb.length - 1 ? (
|
||||
<span>{item.menuName}</span>
|
||||
<div className="d-flex justify-content-between align-items-center mb-3">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol className="breadcrumb mb-0">
|
||||
<li className={`breadcrumb-item ${breadcrumb.length === 0 ? 'active' : ''}`}>
|
||||
{breadcrumb.length === 0 ? (
|
||||
<span><CIcon icon={cilHome} className="me-1" />최상위 메뉴</span>
|
||||
) : (
|
||||
<a
|
||||
href="#"
|
||||
onClick={(e) => { e.preventDefault(); handleBreadcrumbClick(index); }}
|
||||
onClick={(e) => { e.preventDefault(); handleBreadcrumbClick(-1); }}
|
||||
style={{ textDecoration: 'none' }}
|
||||
>
|
||||
{item.menuName}
|
||||
<CIcon icon={cilHome} className="me-1" />최상위 메뉴
|
||||
</a>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
</nav>
|
||||
{breadcrumb.map((item, index) => (
|
||||
<li
|
||||
key={item.adminMenuSeq}
|
||||
className={`breadcrumb-item ${index === breadcrumb.length - 1 ? 'active' : ''}`}
|
||||
>
|
||||
{index === breadcrumb.length - 1 ? (
|
||||
<span>{item.menuName}</span>
|
||||
) : (
|
||||
<a
|
||||
href="#"
|
||||
onClick={(e) => { e.preventDefault(); handleBreadcrumbClick(index); }}
|
||||
style={{ textDecoration: 'none' }}
|
||||
>
|
||||
{item.menuName}
|
||||
</a>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
</nav>
|
||||
<CButton
|
||||
color="primary"
|
||||
size="sm"
|
||||
onClick={handleAddClick}
|
||||
>
|
||||
<CIcon icon={cilPlus} className="me-1" />
|
||||
메뉴 추가
|
||||
</CButton>
|
||||
</div>
|
||||
|
||||
<CTable align="middle" className="mb-0 border" hover responsive style={{ tableLayout: 'fixed', minWidth: isCompact ? '400px' : '650px' }}>
|
||||
<CTable align="middle" className="mb-0 border" hover responsive style={{ tableLayout: 'fixed', minWidth: isCompact ? 'auto' : '650px' }}>
|
||||
<CTableHead className="text-nowrap">
|
||||
<CTableRow>
|
||||
<CTableHeaderCell className="bg-body-tertiary text-center" style={{ width: '80px' }}>메뉴 번호</CTableHeaderCell>
|
||||
<CTableHeaderCell className="bg-body-tertiary text-center" style={{ width: '70px' }}>순번</CTableHeaderCell>
|
||||
{!isCompact && (
|
||||
<CTableHeaderCell className="bg-body-tertiary text-center" style={{ width: '60px' }}>아이콘</CTableHeaderCell>
|
||||
)}
|
||||
<CTableHeaderCell className="bg-body-tertiary text-center" style={{ width: '180px' }}>메뉴 이름</CTableHeaderCell>
|
||||
<CTableHeaderCell className="bg-body-tertiary text-center" style={{ width: '70px' }}>순번</CTableHeaderCell>
|
||||
<CTableHeaderCell className="bg-body-tertiary text-center" style={{ width: isCompact ? 'auto' : '180px' }}>메뉴 이름</CTableHeaderCell>
|
||||
{!isCompact && (
|
||||
<CTableHeaderCell className="bg-body-tertiary text-center" style={{ width: 'auto' }}>URL</CTableHeaderCell>
|
||||
)}
|
||||
@@ -481,22 +495,20 @@ const AdminMenuManagement: React.FC = () => {
|
||||
<CTableBody style={{ minHeight: '200px' }}>
|
||||
{loading ? (
|
||||
<CTableRow>
|
||||
<CTableDataCell colSpan={isCompact ? 4 : 6} className="text-center py-5">
|
||||
<CTableDataCell colSpan={isCompact ? 3 : 5} className="text-center py-5">
|
||||
로딩 중...
|
||||
</CTableDataCell>
|
||||
</CTableRow>
|
||||
) : menuList.length === 0 ? (
|
||||
<CTableRow>
|
||||
<CTableDataCell colSpan={isCompact ? 4 : 6} className="text-center">
|
||||
<CTableDataCell colSpan={isCompact ? 3 : 5} className="text-center">
|
||||
등록된 메뉴가 없습니다.
|
||||
</CTableDataCell>
|
||||
</CTableRow>
|
||||
) : (
|
||||
menuList.map((menu) => (
|
||||
<CTableRow key={menu.adminMenuSeq}>
|
||||
<CTableDataCell className="text-center">
|
||||
<div className="fw-semibold">{menu.adminMenuSeq}</div>
|
||||
</CTableDataCell>
|
||||
<CTableDataCell className="text-center">{menu.menuOrder}</CTableDataCell>
|
||||
{!isCompact && (
|
||||
<CTableDataCell className="text-center">
|
||||
{menu.iconName && getIconByName(menu.iconName) ? (
|
||||
@@ -525,7 +537,6 @@ const AdminMenuManagement: React.FC = () => {
|
||||
</a>
|
||||
</div>
|
||||
</CTableDataCell>
|
||||
<CTableDataCell className="text-center">{menu.menuOrder}</CTableDataCell>
|
||||
{!isCompact && (
|
||||
<CTableDataCell className="text-center">
|
||||
<div
|
||||
@@ -760,19 +771,34 @@ const AdminMenuManagement: React.FC = () => {
|
||||
<CModalTitle>메뉴 삭제</CModalTitle>
|
||||
</CModalHeader>
|
||||
<CModalBody>
|
||||
정말로 <strong>{selectedMenu?.menuName}</strong> 메뉴를 삭제하시겠습니까?
|
||||
<br />
|
||||
<small className="text-danger">
|
||||
하위 메뉴가 있는 경우 함께 삭제되지 않습니다. 먼저 하위 메뉴를 삭제해주세요.
|
||||
</small>
|
||||
{hasChildMenus ? (
|
||||
<>
|
||||
<div className="text-danger mb-2">
|
||||
<CIcon icon={cilWarning} className="me-2" />
|
||||
<strong>삭제할 수 없습니다.</strong>
|
||||
</div>
|
||||
<p>
|
||||
<strong>{selectedMenu?.menuName}</strong> 메뉴에 하위 메뉴가 존재합니다.
|
||||
</p>
|
||||
<small className="text-muted">
|
||||
하위 메뉴를 먼저 모두 삭제한 후 다시 시도해주세요.
|
||||
</small>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
정말로 <strong>{selectedMenu?.menuName}</strong> 메뉴를 삭제하시겠습니까?
|
||||
</>
|
||||
)}
|
||||
</CModalBody>
|
||||
<CModalFooter>
|
||||
<CButton color="secondary" onClick={() => setDeleteModalVisible(false)}>
|
||||
취소
|
||||
</CButton>
|
||||
<CButton color="danger" onClick={handleDelete}>
|
||||
삭제
|
||||
{hasChildMenus ? '닫기' : '취소'}
|
||||
</CButton>
|
||||
{!hasChildMenus && (
|
||||
<CButton color="danger" onClick={handleDelete}>
|
||||
삭제
|
||||
</CButton>
|
||||
)}
|
||||
</CModalFooter>
|
||||
</CModal>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user