diff --git a/frontend/dashboard.css b/frontend/dashboard.css index 675bf52..a6ac3ca 100644 --- a/frontend/dashboard.css +++ b/frontend/dashboard.css @@ -4,6 +4,11 @@ html, body { height: 100%; margin: 0; background: var(--bg); color: var(--text); #bg { position: fixed; inset: 0; z-index: 0; pointer-events: none; opacity: .35; } header { position: sticky; top: 0; z-index: 20; padding: 12px 16px; display:flex; gap:16px; align-items:center; justify-content:space-between; background: linear-gradient(180deg, rgba(11,21,38,.95), rgba(11,21,38,.55), transparent); backdrop-filter: blur(4px); border-bottom: 1px solid #1b2d45; } .toolbar { display:flex; gap:10px; align-items:center; flex-wrap:wrap; } +.app-brand { display:flex; align-items:center; gap:6px; white-space:nowrap; } +.app-live-status { display:inline-flex; gap:6px; align-items:center; } +.app-menu { margin-left:auto; } +.app-menu > summary { display:none; } +.app-menu-panel { display:flex; gap:10px; align-items:center; flex-wrap:wrap; } button { background: linear-gradient(180deg,#1f3a5f,#15324d); border:1px solid #2a496e; color:#e5e7eb; padding:8px 12px; border-radius:10px; cursor:pointer; } #sign-out-all { min-height:44px; } button:hover { filter: brightness(1.15); } @@ -63,6 +68,9 @@ textarea { resize: vertical; min-height: 120px; } .event:last-child { border-bottom: 0; } .my-work { grid-column: 1 / -1; } .my-work-header { display:flex; align-items:center; justify-content:space-between; gap:10px; flex-wrap:wrap; } +.work-settings { width:100%; } +.work-settings > summary { display:none; } +.work-settings-panel { display:flex; align-items:center; gap:10px; flex-wrap:wrap; } .work-filters { display:flex; gap:8px; flex-wrap:wrap; } .work-filter { min-height: 44px; } .work-filter[aria-pressed="true"] { border-color:var(--accent); background:#1d4f7a; } @@ -268,8 +276,22 @@ textarea { resize: vertical; min-height: 120px; } .mobile-task-count { min-width:18px; min-height:18px; padding:1px 5px; border-radius:999px; background:#31577f; font-size:11px; line-height:16px; } @media (max-width: 600px) { body { padding-bottom:calc(66px + env(safe-area-inset-bottom)); } - header { align-items:flex-start; } + header { min-height:56px; max-height:64px; padding:6px 10px; align-items:center; gap:8px; background:rgba(11,21,38,.98); } + .app-brand .muted, #clock { display:none; } + .app-live-status { margin-left:auto; } + .app-menu { position:relative; margin-left:0; } + .app-menu > summary { min-width:44px; min-height:44px; display:grid; place-items:center; cursor:pointer; border:1px solid #2a496e; border-radius:10px; list-style:none; } + .app-menu > summary::-webkit-details-marker { display:none; } + .app-menu:not([open]) > .app-menu-panel { display:none; } + .app-menu-panel { position:absolute; top:calc(100% + 6px); right:0; width:min(280px,calc(100vw - 20px)); display:grid; gap:8px; padding:10px; border:1px solid #2a496e; border-radius:12px; background:#0b1526; box-shadow:0 18px 45px rgba(0,0,0,.5); } + .app-menu-panel button { min-height:44px; width:100%; } .my-work { margin:0; } + .my-work-header { align-items:flex-start; } + .my-work-actions { width:100%; flex-wrap:nowrap; } + .my-work-actions button { min-height:44px; flex:1 1 0; padding-inline:6px; } + .work-settings > summary { min-height:44px; display:flex; align-items:center; cursor:pointer; padding:0 10px; border:1px solid #2a496e; border-radius:10px; font-weight:700; } + .work-settings:not([open]) > .work-settings-panel { display:none; } + .work-settings-panel { display:grid; gap:10px; margin-top:8px; } .my-work-list { grid-template-columns:1fr; } .work-filters { width:100%; } .work-filter { flex:1 1 calc(50% - 8px); } diff --git a/frontend/dashboard.js b/frontend/dashboard.js index e277611..a0fd30c 100644 --- a/frontend/dashboard.js +++ b/frontend/dashboard.js @@ -1,6 +1,12 @@ (function(){ const qs = (s, el=document) => el.querySelector(s); const fmt = (d) => new Date(d).toLocaleString(); + [ + [qs('.app-menu'), qs('#app-menu-toggle')], + [qs('.work-settings'), qs('#work-settings-toggle')], + ].forEach(([disclosure, launcher]) => { + mobileLaunch.createDisclosure({disclosure, launcher}).start(); + }); const PANEL_STATE_KEY = "stackchain.panel-state.v1"; const panels = Array.from(document.querySelectorAll('details[data-panel-key]')); let savedState = {}; @@ -66,9 +72,15 @@ const WORK_MILESTONE_KEY = 'stackchain.my-work-milestone.v1'; let selectedWorkFilter = 'all'; let selectedWorkMilestone = 'all'; + let savedWorkFilter = null; + let launchFilterResolved = false; try { const savedFilter = sessionStorage.getItem(WORK_FILTER_KEY); - if (['all', 'today', 'attention', 'issue', 'pull', 'review', 'update', 'later', 'draft'].includes(savedFilter)) selectedWorkFilter = savedFilter; + if (['all', 'today', 'attention', 'issue', 'pull', 'review', 'update', 'later', 'draft'].includes(savedFilter)) { + selectedWorkFilter = savedFilter; + savedWorkFilter = savedFilter; + launchFilterResolved = true; + } const savedMilestone = sessionStorage.getItem(WORK_MILESTONE_KEY); if (savedMilestone) selectedWorkMilestone = savedMilestone; } catch (e) { @@ -621,11 +633,23 @@ counts.today = todayMyWork.length; counts.later = laterMyWork.length; counts.draft = lastDrafts.length; + if (!launchFilterResolved) { + selectedWorkFilter = mobileLaunch.chooseFilter({ + saved: savedWorkFilter, today: counts.today, attention: counts.attention, + }); + launchFilterResolved = true; + document.querySelectorAll('[data-work-filter]').forEach(item => + item.setAttribute('aria-pressed', String(item.dataset.workFilter === selectedWorkFilter)) + ); + } Object.entries(counts).forEach(([filter, count]) => { const element = qs('[data-work-count="' + filter + '"]'); if (element) element.textContent = count; }); mobileTaskDock.updateAttention(counts.attention); + const activeQueue = qs('[data-work-filter="' + selectedWorkFilter + '"]'); + qs('#active-work-queue').textContent = activeQueue.firstChild.textContent.trim() + + ' (' + (counts[selectedWorkFilter] || 0) + ')'; const milestoneSelect = qs('#work-milestone-filter'); const lanes = milestoneLanes(lastMyWork); milestoneSelect.innerHTML = '' + @@ -2904,6 +2928,8 @@ button.setAttribute('aria-pressed', String(button.dataset.workFilter === selectedWorkFilter)); button.addEventListener('click', () => { selectedWorkFilter = button.dataset.workFilter; + savedWorkFilter = selectedWorkFilter; + launchFilterResolved = true; try { sessionStorage.setItem(WORK_FILTER_KEY, selectedWorkFilter); } catch (e) { @@ -2912,6 +2938,8 @@ document.querySelectorAll('[data-work-filter]').forEach(item => item.setAttribute('aria-pressed', String(item === button)) ); + const selectedCount = button.querySelector('[data-work-count]').textContent; + qs('#active-work-queue').textContent = button.firstChild.textContent.trim() + ' (' + selectedCount + ')'; renderMyWork(); updateWorkPaginationControls(); }); diff --git a/frontend/index.html b/frontend/index.html index b1577d4..cfb81fa 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -11,15 +11,18 @@
Install Stackchain
Keep mobile work one tap away and launch the saved app shell during an outage.
On Safari, tap Share, then choose Add to Home Screen.
- - +Install Stackchain
Keep mobile work one tap away and launch the saved app shell during an outage.
On Safari, tap Share, then choose Add to Home Screen.
+ + +