From 706c8ba52c89969110a7e1d6feed24d96d981b3f Mon Sep 17 00:00:00 2001 From: egg Date: Mon, 9 Feb 2026 10:16:14 +0800 Subject: [PATCH] feat(portal): refactor navigation from drawer to sidebar layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace collapsible
drawers with a persistent left sidebar for 報表類, 查詢類, and 開發工具 categories. Unify dev tools handling via data-tool-src attribute instead of onclick openTool(). Also release tmtt-defect page status from dev to released. Co-Authored-By: Claude Opus 4.6 --- data/page_status.json | 4 +- frontend/src/portal/main.js | 53 ++-- frontend/src/portal/portal.css | 66 +++-- src/mes_dashboard/templates/portal.html | 308 +++++++++++------------- 4 files changed, 210 insertions(+), 221 deletions(-) diff --git a/data/page_status.json b/data/page_status.json index f6d87a6..32559d7 100644 --- a/data/page_status.json +++ b/data/page_status.json @@ -53,7 +53,7 @@ { "route": "/tmtt-defect", "name": "TMTT印字腳型不良分析", - "status": "dev" + "status": "released" } ], "api_public": true, @@ -63,4 +63,4 @@ "object_count": 19, "source": "tools/query_table_schema.py" } -} +} \ No newline at end of file diff --git a/frontend/src/portal/main.js b/frontend/src/portal/main.js index fd4be83..ebeb23c 100644 --- a/frontend/src/portal/main.js +++ b/frontend/src/portal/main.js @@ -1,13 +1,11 @@ import './portal.css'; (function initPortal() { - const tabs = document.querySelectorAll('.tab'); + const sidebarItems = document.querySelectorAll('.sidebar-item[data-target]'); const frames = document.querySelectorAll('iframe'); - const toolFrame = document.getElementById('toolFrame'); const healthDot = document.getElementById('healthDot'); const healthLabel = document.getElementById('healthLabel'); const healthPopup = document.getElementById('healthPopup'); - const healthStatus = document.getElementById('healthStatus'); const dbStatus = document.getElementById('dbStatus'); const redisStatus = document.getElementById('redisStatus'); const cacheEnabled = document.getElementById('cacheEnabled'); @@ -22,41 +20,37 @@ import './portal.css'; function setFrameHeight() { const header = document.querySelector('.header'); - const tabArea = document.querySelector('.tabs'); - if (!header || !tabArea) return; - const height = Math.max(600, window.innerHeight - header.offsetHeight - tabArea.offsetHeight - 60); + if (!header) return; + const height = Math.max(600, window.innerHeight - header.offsetHeight - 52); frames.forEach((frame) => { frame.style.height = `${height}px`; }); } - function activateTab(targetId) { - tabs.forEach((tab) => tab.classList.remove('active')); + function activateTab(targetId, toolSrc) { + sidebarItems.forEach((item) => item.classList.remove('active')); frames.forEach((frame) => frame.classList.remove('active')); - const tabBtn = document.querySelector(`[data-target="${targetId}"]`); - const targetFrame = document.getElementById(targetId); + const activeItems = document.querySelectorAll(`.sidebar-item[data-target="${targetId}"]`); + activeItems.forEach((item) => { + if (!toolSrc || item.dataset.toolSrc === toolSrc) { + item.classList.add('active'); + } + }); - if (tabBtn) tabBtn.classList.add('active'); + const targetFrame = document.getElementById(targetId); if (targetFrame) { targetFrame.classList.add('active'); - if (targetFrame.dataset.src && !targetFrame.src) { + if (toolSrc) { + if (targetFrame.src !== toolSrc && !targetFrame.src.endsWith(toolSrc)) { + targetFrame.src = toolSrc; + } + } else if (targetFrame.dataset.src && !targetFrame.src) { targetFrame.src = targetFrame.dataset.src; } } } - function openTool(path) { - if (!toolFrame) return false; - tabs.forEach((tab) => tab.classList.remove('active')); - frames.forEach((frame) => frame.classList.remove('active')); - toolFrame.classList.add('active'); - if (toolFrame.src !== path) { - toolFrame.src = path; - } - return false; - } - function toggleHealthPopup() { if (!healthPopup) return; healthPopup.classList.toggle('show'); @@ -167,18 +161,17 @@ import './portal.css'; } } - tabs.forEach((tab) => { - tab.addEventListener('click', () => activateTab(tab.dataset.target)); + sidebarItems.forEach((item) => { + item.addEventListener('click', () => { + activateTab(item.dataset.target, item.dataset.toolSrc || null); + }); }); - if (tabs.length > 0) { - activateTab(tabs[0].dataset.target); + if (sidebarItems.length > 0) { + activateTab(sidebarItems[0].dataset.target); } - window.openTool = openTool; window.toggleHealthPopup = toggleHealthPopup; - // Click handler is wired via inline onclick in template for fallback compatibility. - // Avoid duplicate binding here, otherwise a single click toggles twice. document.addEventListener('click', (e) => { if (!e.target.closest('#healthStatus') && !e.target.closest('#healthPopup') && healthPopup) { healthPopup.classList.remove('show'); diff --git a/frontend/src/portal/portal.css b/frontend/src/portal/portal.css index 97ac7c6..687ea28 100644 --- a/frontend/src/portal/portal.css +++ b/frontend/src/portal/portal.css @@ -1,29 +1,55 @@ -.drawer { +/* Sidebar Navigation */ +.sidebar { + width: 200px; + min-width: 200px; background: #fff; border-radius: 10px; border: 1px solid #e3e8f2; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06); - overflow: hidden; + overflow-y: auto; + align-self: flex-start; + position: sticky; + top: 20px; } -.drawer > summary { - list-style: none; +.sidebar-group-title { + padding: 10px 14px 6px; + font-size: 11px; + font-weight: 700; + color: #94a3b8; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.sidebar-group-title:not(:first-child) { + border-top: 1px solid #e2e8f0; + margin-top: 4px; + padding-top: 12px; +} + +.sidebar-item { + display: block; + width: 100%; + border: none; + background: none; + text-align: left; + padding: 8px 14px; + font-size: 13px; + color: #475569; cursor: pointer; - padding: 10px 14px; - font-size: 14px; + transition: all 0.15s ease; + text-decoration: none; + font-family: inherit; +} + +.sidebar-item:hover { + background: #f1f5f9; + color: #667eea; +} + +.sidebar-item.active { + background: #eef2ff; + color: #667eea; font-weight: 600; - color: #334155; - background: #f8fafc; - border-bottom: 1px solid #e2e8f0; -} - -.drawer > summary::-webkit-details-marker { - display: none; -} - -.drawer-content { - display: flex; - flex-wrap: wrap; - gap: 10px; - padding: 12px; + border-right: 3px solid #667eea; } diff --git a/src/mes_dashboard/templates/portal.html b/src/mes_dashboard/templates/portal.html index 2e4051d..ccb56d1 100644 --- a/src/mes_dashboard/templates/portal.html +++ b/src/mes_dashboard/templates/portal.html @@ -214,86 +214,75 @@ position: relative; } - .tabs { - margin-bottom: 12px; - display: grid; - gap: 10px; + /* Sidebar + Panel Layout */ + .main-layout { + display: flex; + gap: 12px; + min-height: calc(100vh - 140px); } - .drawer { + .sidebar { + width: 200px; + min-width: 200px; background: white; border-radius: 10px; border: 1px solid #e3e8f2; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06); - overflow: hidden; + overflow-y: auto; + align-self: flex-start; + position: sticky; + top: 20px; } - .drawer > summary { - list-style: none; + .sidebar-group-title { + padding: 10px 14px 6px; + font-size: 11px; + font-weight: 700; + color: #94a3b8; + text-transform: uppercase; + letter-spacing: 0.5px; + } + + .sidebar-group-title:not(:first-child) { + border-top: 1px solid #e2e8f0; + margin-top: 4px; + padding-top: 12px; + } + + .sidebar-item { + display: block; + width: 100%; + border: none; + background: none; + text-align: left; + padding: 8px 14px; + font-size: 13px; + color: #475569; cursor: pointer; - padding: 10px 14px; - font-size: 14px; - font-weight: 600; - color: #334155; - background: #f8fafc; - border-bottom: 1px solid #e2e8f0; - } - - .drawer > summary::-webkit-details-marker { - display: none; - } - - .drawer-content { - display: flex; - flex-wrap: wrap; - gap: 10px; - padding: 12px; - } - - .tab { - border: 2px solid #d9d9d9; - background: white; - color: #333; - padding: 10px 18px; - border-radius: 8px; - cursor: pointer; - font-size: 14px; - transition: all 0.2s ease; - } - - .tab:hover { - border-color: #667eea; - color: #667eea; - } - - .tab.active { - background: #667eea; - border-color: #667eea; - color: white; - } - - .drawer-link { - border: 1px solid #d9d9d9; - background: white; - color: #333; - padding: 10px 18px; - border-radius: 8px; - cursor: pointer; - font-size: 14px; + transition: all 0.15s ease; text-decoration: none; - transition: all 0.2s ease; + font-family: inherit; } - .drawer-link:hover { - border-color: #667eea; + .sidebar-item:hover { + background: #f1f5f9; color: #667eea; } + .sidebar-item.active { + background: #eef2ff; + color: #667eea; + font-weight: 600; + border-right: 3px solid #667eea; + } + .panel { + flex: 1; background: white; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.08); overflow: hidden; + min-width: 0; } .panel iframe { @@ -363,85 +352,74 @@ -
-
- 報表類 -
- {% if can_view_page('/wip-overview') %} - - {% endif %} - {% if can_view_page('/resource') %} - - {% endif %} - {% if can_view_page('/resource-history') %} - - {% endif %} -
-
+
+
+ + {% if is_admin %} + + + {% else %} + 管理員登入 + {% endif %} + -
- - {% if can_view_page('/wip-overview') %} - - {% endif %} - {% if can_view_page('/resource') %} - - {% endif %} - {% if can_view_page('/tables') %} - - {% endif %} - {% if can_view_page('/excel-query') %} - - {% endif %} - {% if can_view_page('/resource-history') %} - - {% endif %} - {% if can_view_page('/job-query') %} - - {% endif %} - {% if can_view_page('/query-tool') %} - - {% endif %} - {% if can_view_page('/tmtt-defect') %} - - {% endif %} - {% if is_admin %} - - {% endif %} +
+ {% if can_view_page('/wip-overview') %} + + {% endif %} + {% if can_view_page('/resource') %} + + {% endif %} + {% if can_view_page('/tables') %} + + {% endif %} + {% if can_view_page('/excel-query') %} + + {% endif %} + {% if can_view_page('/resource-history') %} + + {% endif %} + {% if can_view_page('/job-query') %} + + {% endif %} + {% if can_view_page('/query-tool') %} + + {% endif %} + {% if can_view_page('/tmtt-defect') %} + + {% endif %} + {% if is_admin %} + + {% endif %} +
{% endblock %} @@ -452,61 +430,53 @@ {% else %}