From fd50a6dba06dd6dc779c668798d1cb4634eb6545 Mon Sep 17 00:00:00 2001 From: kent Date: Sun, 21 Dec 2025 01:31:06 +0900 Subject: [PATCH] =?UTF-8?q?fix(mng):=20=EB=A9=94=EB=89=B4=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20UI=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=EB=84=A4=EC=9D=B4=EC=85=98=20=EC=BF=A0?= =?UTF-8?q?=ED=82=A4=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 메뉴 등록/수정: 부모 메뉴 선택에서 아이콘 제거 (메뉴명만 표시) - 글로벌 메뉴: 아이콘 그리드 선택기, 확장 옵션(section, meta) 추가 - 페이지네이션: per_page 쿠키 값이 서버 기본값으로 덮어쓰이는 버그 수정 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- public/js/pagination.js | 10 +- resources/views/menus/create.blade.php | 2 +- resources/views/menus/edit.blade.php | 2 +- resources/views/menus/global-create.blade.php | 162 +++++++++++++++- resources/views/menus/global-edit.blade.php | 180 +++++++++++++++++- 5 files changed, 336 insertions(+), 20 deletions(-) diff --git a/public/js/pagination.js b/public/js/pagination.js index 7f668007..2f620118 100644 --- a/public/js/pagination.js +++ b/public/js/pagination.js @@ -114,13 +114,9 @@ document.body.addEventListener('htmx:afterSwap', function(event) { setTimeout(function() { const perPageSelect = document.getElementById('perPageSelect'); if (perPageSelect) { - // 서버에서 받은 값(data-server-value)을 우선 사용 - const serverValue = perPageSelect.dataset.serverValue; - if (serverValue) { - perPageSelect.value = serverValue; - // 쿠키도 서버 값으로 업데이트 - setCookie('pagination_per_page', serverValue); - } + // 쿠키에 저장된 값으로 selectbox 설정 (서버 값으로 덮어쓰지 않음) + const cookieValue = getPerPageFromCookie(); + perPageSelect.value = cookieValue; } }, 50); }); diff --git a/resources/views/menus/create.blade.php b/resources/views/menus/create.blade.php index a4ebbe5e..990f80fd 100644 --- a/resources/views/menus/create.blade.php +++ b/resources/views/menus/create.blade.php @@ -30,7 +30,7 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none foc @foreach($parentMenus as $parent) @endforeach diff --git a/resources/views/menus/edit.blade.php b/resources/views/menus/edit.blade.php index 6ead1259..9d034aba 100644 --- a/resources/views/menus/edit.blade.php +++ b/resources/views/menus/edit.blade.php @@ -32,7 +32,7 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none foc @foreach($parentMenus as $parent) @if($parent->id != $menu->id) @endif @endforeach diff --git a/resources/views/menus/global-create.blade.php b/resources/views/menus/global-create.blade.php index 5de470f0..0b61f7e3 100644 --- a/resources/views/menus/global-create.blade.php +++ b/resources/views/menus/global-create.blade.php @@ -7,7 +7,10 @@ @@ -58,9 +63,39 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none foc - +
+
+ +
+ +
+ +
+

사용 가능한 아이콘 (클릭하여 선택)

+
+ @php + $sampleIcons = [ + 'home', 'folder', 'chart-bar', 'calendar', 'building', + 'users', 'user-group', 'menu', 'shield-check', 'key', + 'cog', 'beaker', 'code', 'document-text', 'clipboard-list', + 'cube', 'collection', 'tag', 'database', 'terminal', + 'server', 'adjustments', 'sparkles', 'lightning-bolt', 'puzzle', 'external-link', + ]; + @endphp + @foreach($sampleIcons as $iconKey) + + @endforeach +
+
@@ -112,6 +147,43 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none foc placeholder="https://example.com"> + +
+
+

확장 옵션

+ +
+ +
+ +
+ + +

사이드바 영역 구분. main: 메인 메뉴, tools: 개발도구 영역, labs: R&D 실험실 영역

+
+ + +
+ + +

추가 메타 데이터 (JSON 형식). 탭 구분, 뱃지 표시 등 커스텀 속성 저장

+
+
+
+
${iconKey}`; + } + }; + // 외부 링크 체크박스 토글 document.getElementById('is_external').addEventListener('change', function() { const externalUrlGroup = document.getElementById('external-url-group'); @@ -140,6 +243,41 @@ class="px-6 py-2 bg-purple-600 hover:bg-purple-700 text-white rounded-lg transit } }); + // 확장 옵션 섹션 토글 + window.toggleOptionsSection = function() { + const section = document.getElementById('options-section'); + const toggleText = document.getElementById('options-toggle-text'); + if (section.classList.contains('hidden')) { + section.classList.remove('hidden'); + toggleText.textContent = '접기'; + } else { + section.classList.add('hidden'); + toggleText.textContent = '펼치기'; + } + }; + + // options 객체 생성 + function buildOptionsObject() { + const options = {}; + + const section = document.getElementById('option_section').value; + if (section && section !== 'main') { + options.section = section; + } + + const metaStr = document.getElementById('option_meta').value.trim(); + if (metaStr) { + try { + options.meta = JSON.parse(metaStr); + } catch (e) { + showToast('meta 필드의 JSON 형식이 올바르지 않습니다.', 'error'); + throw e; + } + } + + return Object.keys(options).length > 0 ? options : null; + } + // 폼 제출 처리 document.getElementById('menuForm').addEventListener('submit', async function(e) { e.preventDefault(); @@ -147,6 +285,20 @@ class="px-6 py-2 bg-purple-600 hover:bg-purple-700 text-white rounded-lg transit const formData = new FormData(this); const data = Object.fromEntries(formData.entries()); + // option_ 접두사 필드 제거 + delete data.option_section; + delete data.option_meta; + + // options 객체 추가 + try { + const options = buildOptionsObject(); + if (options) { + data.options = options; + } + } catch (e) { + return; // JSON 파싱 에러 + } + try { const response = await fetch('/api/admin/global-menus', { method: 'POST', diff --git a/resources/views/menus/global-edit.blade.php b/resources/views/menus/global-edit.blade.php index 4603956b..115dba73 100644 --- a/resources/views/menus/global-edit.blade.php +++ b/resources/views/menus/global-edit.blade.php @@ -7,7 +7,10 @@ + @php + $currentIcon = old('icon', $menu->icon); + $sampleIcons = [ + 'home', 'folder', 'chart-bar', 'calendar', 'building', + 'users', 'user-group', 'menu', 'shield-check', 'key', + 'cog', 'beaker', 'code', 'document-text', 'clipboard-list', + 'cube', 'collection', 'tag', 'database', 'terminal', + 'server', 'adjustments', 'sparkles', 'lightning-bolt', 'puzzle', 'external-link', + ]; + @endphp
- +
+
+ @if($currentIcon && in_array($currentIcon, $sampleIcons)) + + @elseif($currentIcon) + {{ $currentIcon }} + @else + + @endif +
+ +
+ +
+

사용 가능한 아이콘 (클릭하여 선택)

+
+ @foreach($sampleIcons as $iconKey) + + @endforeach +
+
@@ -125,6 +165,54 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none foc placeholder="https://example.com">
+ + @php + $hasOptions = $menu->options && count($menu->options) > 0; + $currentSection = $menu->getSection() ?? 'main'; + $currentMeta = $menu->getMeta(); + $currentMetaJson = $currentMeta ? json_encode($currentMeta, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : ''; + @endphp +
+
+

+ 확장 옵션 + @if($hasOptions) + {{ count($menu->options) }} + @endif +

+ +
+ +
+ +
+ + +

사이드바 영역 구분. main: 메인 메뉴, tools: 개발도구 영역, labs: R&D 실험실 영역

+
+ + +
+ + +

추가 메타 데이터 (JSON 형식). 탭 구분, 뱃지 표시 등 커스텀 속성 저장

+
+
+
+
${iconKey}`; + } + }; + // 외부 링크 체크박스 토글 document.getElementById('is_external').addEventListener('change', function() { const externalUrlGroup = document.getElementById('external-url-group'); @@ -153,6 +272,41 @@ class="px-6 py-2 bg-purple-600 hover:bg-purple-700 text-white rounded-lg transit } }); + // 확장 옵션 섹션 토글 + window.toggleOptionsSection = function() { + const section = document.getElementById('options-section'); + const toggleText = document.getElementById('options-toggle-text'); + if (section.classList.contains('hidden')) { + section.classList.remove('hidden'); + toggleText.textContent = '접기'; + } else { + section.classList.add('hidden'); + toggleText.textContent = '펼치기'; + } + }; + + // options 객체 생성 + function buildOptionsObject() { + const options = {}; + + const section = document.getElementById('option_section').value; + if (section && section !== 'main') { + options.section = section; + } + + const metaStr = document.getElementById('option_meta').value.trim(); + if (metaStr) { + try { + options.meta = JSON.parse(metaStr); + } catch (e) { + showToast('meta 필드의 JSON 형식이 올바르지 않습니다.', 'error'); + throw e; + } + } + + return Object.keys(options).length > 0 ? options : null; + } + // 폼 제출 처리 document.getElementById('menuForm').addEventListener('submit', async function(e) { e.preventDefault(); @@ -160,6 +314,20 @@ class="px-6 py-2 bg-purple-600 hover:bg-purple-700 text-white rounded-lg transit const formData = new FormData(this); const data = Object.fromEntries(formData.entries()); + // option_ 접두사 필드 제거 + delete data.option_section; + delete data.option_meta; + + // options 객체 추가 + try { + const options = buildOptionsObject(); + if (options) { + data.options = options; + } + } catch (e) { + return; // JSON 파싱 에러 + } + try { const response = await fetch('/api/admin/global-menus/{{ $menu->id }}', { method: 'PUT',