diff --git a/public/js/menu-tree.js b/public/js/menu-tree.js
index 8dbe70d3..95e8cdfb 100644
--- a/public/js/menu-tree.js
+++ b/public/js/menu-tree.js
@@ -6,127 +6,119 @@
* - 메뉴 관리 (menus) - 테이블 기반
*
* localStorage 키: 'menu-tree-collapsed' (접힌 메뉴 ID 배열)
+ * IIFE로 감싸서 HTMX swap 시 재실행해도 const 재선언 에러 방지
*/
-const MENU_TREE_STORAGE_KEY = 'menu-tree-collapsed';
+(function() {
+ 'use strict';
-// localStorage에서 접힌 메뉴 ID Set 로드
-function getCollapsedMenuIds() {
- try {
- const data = localStorage.getItem(MENU_TREE_STORAGE_KEY);
- return data ? new Set(JSON.parse(data)) : new Set();
- } catch (e) {
- return new Set();
- }
-}
+ const MENU_TREE_STORAGE_KEY = 'menu-tree-collapsed';
-// localStorage에 접힌 메뉴 ID Set 저장
-function saveCollapsedMenuIds(collapsedSet) {
- try {
- localStorage.setItem(MENU_TREE_STORAGE_KEY, JSON.stringify([...collapsedSet]));
- } catch (e) {
- // storage full 등 무시
- }
-}
-
-// 자식 메뉴 접기/펼치기
-window.toggleChildren = function(menuId) {
- const button = document.querySelector(`.toggle-btn[data-menu-id="${menuId}"]`);
- if (!button) return;
-
- const chevron = button.querySelector('.chevron-icon');
- if (!chevron) return;
-
- const collapsedSet = getCollapsedMenuIds();
- const isCollapsed = chevron.classList.contains('rotate-[-90deg]');
-
- if (isCollapsed) {
- // 펼치기
- chevron.classList.remove('rotate-[-90deg]');
- showChildren(menuId);
- collapsedSet.delete(String(menuId));
- } else {
- // 접기
- chevron.classList.add('rotate-[-90deg]');
- hideChildren(menuId);
- collapsedSet.add(String(menuId));
- }
-
- saveCollapsedMenuIds(collapsedSet);
-};
-
-// 자식 요소 선택자 (테이블: tr.menu-row, div: .menu-item)
-function getChildElements(parentId) {
- // 테이블 기반 (tr.menu-row)
- let children = document.querySelectorAll(`tr.menu-row[data-parent-id="${parentId}"]`);
- if (children.length > 0) return children;
-
- // div 기반 (.menu-item)
- return document.querySelectorAll(`.menu-item[data-parent-id="${parentId}"]`);
-}
-
-// 재귀적으로 자식 메뉴 숨기기
-function hideChildren(parentId) {
- const children = getChildElements(parentId);
- children.forEach(child => {
- child.style.display = 'none';
- const childId = child.getAttribute('data-menu-id');
- hideChildren(childId);
- });
-}
-
-// 전체 접기/펼치기
-window.toggleAllChildren = function(collapse) {
- const buttons = document.querySelectorAll('.toggle-btn');
- const collapsedSet = collapse ? new Set() : new Set();
-
- buttons.forEach(btn => {
- const menuId = btn.getAttribute('data-menu-id');
- const chevron = btn.querySelector('.chevron-icon');
- if (!chevron) return;
-
- if (collapse) {
- chevron.classList.add('rotate-[-90deg]');
- hideChildren(menuId);
- collapsedSet.add(String(menuId));
- } else {
- chevron.classList.remove('rotate-[-90deg]');
- showChildren(menuId);
+ function getCollapsedMenuIds() {
+ try {
+ const data = localStorage.getItem(MENU_TREE_STORAGE_KEY);
+ return data ? new Set(JSON.parse(data)) : new Set();
+ } catch (e) {
+ return new Set();
}
- });
+ }
- saveCollapsedMenuIds(collapsedSet);
-};
+ function saveCollapsedMenuIds(collapsedSet) {
+ try {
+ localStorage.setItem(MENU_TREE_STORAGE_KEY, JSON.stringify([...collapsedSet]));
+ } catch (e) {
+ // storage full 등 무시
+ }
+ }
-// 재귀적으로 직계 자식만 표시
-function showChildren(parentId) {
- const children = getChildElements(parentId);
- children.forEach(child => {
- child.style.display = '';
- const childId = child.getAttribute('data-menu-id');
- const childButton = child.querySelector(`.toggle-btn[data-menu-id="${childId}"]`);
- if (childButton) {
- const chevron = childButton.querySelector('.chevron-icon');
- if (chevron && !chevron.classList.contains('rotate-[-90deg]')) {
- showChildren(childId);
+ function getChildElements(parentId) {
+ let children = document.querySelectorAll(`tr.menu-row[data-parent-id="${parentId}"]`);
+ if (children.length > 0) return children;
+ return document.querySelectorAll(`.menu-item[data-parent-id="${parentId}"]`);
+ }
+
+ function hideChildren(parentId) {
+ const children = getChildElements(parentId);
+ children.forEach(child => {
+ child.style.display = 'none';
+ const childId = child.getAttribute('data-menu-id');
+ hideChildren(childId);
+ });
+ }
+
+ function showChildren(parentId) {
+ const children = getChildElements(parentId);
+ children.forEach(child => {
+ child.style.display = '';
+ const childId = child.getAttribute('data-menu-id');
+ const childButton = child.querySelector(`.toggle-btn[data-menu-id="${childId}"]`);
+ if (childButton) {
+ const chevron = childButton.querySelector('.chevron-icon');
+ if (chevron && !chevron.classList.contains('rotate-[-90deg]')) {
+ showChildren(childId);
+ }
}
- }
- });
-}
+ });
+ }
-// HTMX 리로드 후 접힌 상태 복원
-window.restoreMenuTreeState = function() {
- const collapsedSet = getCollapsedMenuIds();
- if (collapsedSet.size === 0) return;
-
- collapsedSet.forEach(menuId => {
+ window.toggleChildren = function(menuId) {
const button = document.querySelector(`.toggle-btn[data-menu-id="${menuId}"]`);
if (!button) return;
const chevron = button.querySelector('.chevron-icon');
- if (chevron) {
+ if (!chevron) return;
+
+ const collapsedSet = getCollapsedMenuIds();
+ const isCollapsed = chevron.classList.contains('rotate-[-90deg]');
+
+ if (isCollapsed) {
+ chevron.classList.remove('rotate-[-90deg]');
+ showChildren(menuId);
+ collapsedSet.delete(String(menuId));
+ } else {
chevron.classList.add('rotate-[-90deg]');
hideChildren(menuId);
+ collapsedSet.add(String(menuId));
}
- });
-};
\ No newline at end of file
+
+ saveCollapsedMenuIds(collapsedSet);
+ };
+
+ window.toggleAllChildren = function(collapse) {
+ const buttons = document.querySelectorAll('.toggle-btn');
+ const collapsedSet = collapse ? new Set() : new Set();
+
+ buttons.forEach(btn => {
+ const menuId = btn.getAttribute('data-menu-id');
+ const chevron = btn.querySelector('.chevron-icon');
+ if (!chevron) return;
+
+ if (collapse) {
+ chevron.classList.add('rotate-[-90deg]');
+ hideChildren(menuId);
+ collapsedSet.add(String(menuId));
+ } else {
+ chevron.classList.remove('rotate-[-90deg]');
+ showChildren(menuId);
+ }
+ });
+
+ saveCollapsedMenuIds(collapsedSet);
+ };
+
+ window.restoreMenuTreeState = function() {
+ const collapsedSet = getCollapsedMenuIds();
+ if (collapsedSet.size === 0) return;
+
+ collapsedSet.forEach(menuId => {
+ const button = document.querySelector(`.toggle-btn[data-menu-id="${menuId}"]`);
+ if (!button) return;
+
+ const chevron = button.querySelector('.chevron-icon');
+ if (chevron) {
+ chevron.classList.add('rotate-[-90deg]');
+ hideChildren(menuId);
+ }
+ });
+ };
+})();
\ No newline at end of file
diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php
index df32b6a3..bc27146f 100644
--- a/resources/views/layouts/app.blade.php
+++ b/resources/views/layouts/app.blade.php
@@ -126,6 +126,10 @@
})();
+