From b84cc8894fb620d1165ee5fd6e2e824a22764b25 Mon Sep 17 00:00:00 2001 From: Louis King Date: Mon, 4 May 2026 11:00:04 +0100 Subject: [PATCH] fix: resolve mobile UI issues on members page and node detail - Replace nested tags in member cards with @click handlers to fix invalid HTML causing missing badge styles and double-navigation race conditions (NS_BINDING_ABORT) on mobile - Add e.preventDefault() to @click handlers to prevent parent activation - Bump mobile dropdown z-index from z-[1] to z-50 so menu items render above QR code wrapper (z-20) on node detail hero section - Blur active element after SPA navigation to dismiss DaisyUI :focus-within dropdown state - Fix dashboard channel messages to show short date+time format - Add overflow-x-auto to custom pages and node detail panels for mobile overflow - Fix tag editor: hide type column on mobile, truncate key/value cells, show key in modal title instead of disabled input field - Fix delete tag dialog using innerHTML for in i18n strings - Fix tag add form not resetting after successful submit - Add break-word and overflow-x-auto to prose tables in app.css --- src/meshcore_hub/web/static/css/app.css | 7 +++++ .../web/static/js/spa/pages/custom-page.js | 2 +- .../web/static/js/spa/pages/dashboard.js | 1 + .../web/static/js/spa/pages/members.js | 19 ++++++++----- .../web/static/js/spa/pages/node-detail.js | 28 +++++++++---------- src/meshcore_hub/web/static/js/spa/router.js | 3 ++ src/meshcore_hub/web/templates/spa.html | 2 +- 7 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/meshcore_hub/web/static/css/app.css b/src/meshcore_hub/web/static/css/app.css index 0c00839..b42f892 100644 --- a/src/meshcore_hub/web/static/css/app.css +++ b/src/meshcore_hub/web/static/css/app.css @@ -256,10 +256,17 @@ font-style: italic; } +.prose { + overflow-wrap: break-word; + word-break: break-word; +} + .prose table { width: 100%; margin-bottom: 1rem; border-collapse: collapse; + display: block; + overflow-x: auto; } .prose th, diff --git a/src/meshcore_hub/web/static/js/spa/pages/custom-page.js b/src/meshcore_hub/web/static/js/spa/pages/custom-page.js index 3f5257b..57f9881 100644 --- a/src/meshcore_hub/web/static/js/spa/pages/custom-page.js +++ b/src/meshcore_hub/web/static/js/spa/pages/custom-page.js @@ -12,7 +12,7 @@ export async function render(container, params, router) { litRender(html`
-
+
${unsafeHTML(page.content_html)}
diff --git a/src/meshcore_hub/web/static/js/spa/pages/dashboard.js b/src/meshcore_hub/web/static/js/spa/pages/dashboard.js index a9f3e8a..47e1435 100644 --- a/src/meshcore_hub/web/static/js/spa/pages/dashboard.js +++ b/src/meshcore_hub/web/static/js/spa/pages/dashboard.js @@ -25,6 +25,7 @@ function formatTimeOnly(isoString) { function formatTimeShort(isoString) { return formatDateTime(isoString, { + month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', hour12: false, }); diff --git a/src/meshcore_hub/web/static/js/spa/pages/members.js b/src/meshcore_hub/web/static/js/spa/pages/members.js index f30c239..a0a230e 100644 --- a/src/meshcore_hub/web/static/js/spa/pages/members.js +++ b/src/meshcore_hub/web/static/js/spa/pages/members.js @@ -2,7 +2,7 @@ import { apiGet } from '../api.js'; import { html, litRender, nothing, t, errorAlert, getConfig } from '../components.js'; import { iconAntenna, iconUsers } from '../icons.js'; -function renderProfileTile(profile) { +function renderProfileTile(profile, router) { const callsignBadge = profile.callsign ? html`${profile.callsign}` : nothing; @@ -20,7 +20,12 @@ function renderProfileTile(profile) { const nodeBadges = profile.adopted_nodes && profile.adopted_nodes.length > 0 ? html`
${profile.adopted_nodes.map(node => { const label = node.name || node.public_key.slice(0, 12) + '...'; - return html`${label}`; + const handleClick = (e) => { + e.preventDefault(); + e.stopPropagation(); + router.navigate('/nodes/' + node.public_key); + }; + return html`${label}`; })}
` : nothing; @@ -29,7 +34,7 @@ function renderProfileTile(profile) { : nothing; const urlLink = profile.url - ? html`${profile.url}` + ? html` { e.preventDefault(); e.stopPropagation(); window.open(profile.url, '_blank', 'noopener,noreferrer'); }}>${profile.url}` : nothing; return html` @@ -47,14 +52,14 @@ function renderProfileTile(profile) { `; } -function renderGroup(title, profiles, icon) { +function renderGroup(title, profiles, icon, router) { if (profiles.length === 0) return nothing; return html`

${icon}${title}

- ${profiles.sort((a, b) => (a.name || '').localeCompare(b.name || '')).map(renderProfileTile)} + ${profiles.sort((a, b) => (a.name || '').localeCompare(b.name || '')).map(p => renderProfileTile(p, router))}
`; } @@ -92,8 +97,8 @@ export async function render(container, params, router) { ${t('common.count_entity', { count: profiles.length, entity: t('entities.members').toLowerCase() })}
-${renderGroup(t('members_page.operators'), operators, html`${iconAntenna('h-6 w-6')}`)} -${renderGroup(t('members_page.members'), members, html`${iconUsers('h-6 w-6')}`)} +${renderGroup(t('members_page.operators'), operators, html`${iconAntenna('h-6 w-6')}`, router)} +${renderGroup(t('members_page.members'), members, html`${iconUsers('h-6 w-6')}`, router)} `, container); } catch (e) { diff --git a/src/meshcore_hub/web/static/js/spa/pages/node-detail.js b/src/meshcore_hub/web/static/js/spa/pages/node-detail.js index 47376fe..3372355 100644 --- a/src/meshcore_hub/web/static/js/spa/pages/node-detail.js +++ b/src/meshcore_hub/web/static/js/spa/pages/node-detail.js @@ -44,12 +44,9 @@ function renderEditTagModal() { return html`