From 5ac88271af7970a2fcffe08401168ee4d6bd91bc Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Sun, 10 May 2026 01:46:48 +0200 Subject: [PATCH] feat(inbounds): mobile card layout for inbounds and clients Replace the cramped on <768px with a stacked card list for both inbounds and the per-client expanded rows. Each card surfaces protocol, port, node, traffic, all-time traffic, client count and expiry inline as labeled rows instead of hiding them behind popovers, fixes the 0px gutter that made cards visually merge, and softens the in-quota green from #52c41a to #389e0a (Ant green-7) so traffic tags are no longer blinding on dark themes. --- .../src/pages/inbounds/ClientRowTable.vue | 507 +++++++++--------- frontend/src/pages/inbounds/InboundList.vue | 341 +++++++++--- frontend/src/pages/inbounds/InboundsPage.vue | 12 +- frontend/src/utils/index.js | 2 +- 4 files changed, 532 insertions(+), 330 deletions(-) diff --git a/frontend/src/pages/inbounds/ClientRowTable.vue b/frontend/src/pages/inbounds/ClientRowTable.vue index 9d388209..4091c78a 100644 --- a/frontend/src/pages/inbounds/ClientRowTable.vue +++ b/frontend/src/pages/inbounds/ClientRowTable.vue @@ -166,22 +166,20 @@ function rowKey(client) { @@ -510,8 +622,7 @@ function showQrCodeMenu(dbInbound) { gap: 4px; } -.row-action-trigger, -.row-info-trigger { +.row-action-trigger { font-size: 20px; cursor: pointer; } @@ -566,54 +677,124 @@ function showQrCodeMenu(dbInbound) { border-end-end-radius: 8px; } -/* ===== Mobile-tightening ============================================ - * Below 768px the inbound list is on a tiny viewport — squeeze the - * card chrome and table cell padding so the actual rows have room. */ +/* ===== Mobile card list =========================================== + * <768px renders inbounds as a vertical stack of cards via the + * v-if="isMobile" branch above; the desktop isn't mounted + * so the legacy table-cell tightening rules went away. */ +.inbound-cards { + display: flex; + flex-direction: column; + gap: 12px; + margin-top: 4px; +} + +.inbound-card { + border: 1px solid rgba(128, 128, 128, 0.2); + border-radius: 10px; + padding: 12px; + background: rgba(255, 255, 255, 0.02); + display: flex; + flex-direction: column; + gap: 8px; +} +:global(body.dark) .inbound-card { + background: rgba(255, 255, 255, 0.03); + border-color: rgba(255, 255, 255, 0.1); +} + +.card-head { + display: flex; + align-items: center; + gap: 8px; + cursor: pointer; + user-select: none; +} +.card-id { + font-size: 11px; + opacity: 0.6; +} +.tag-name { + font-weight: 600; + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.card-actions { + display: flex; + align-items: center; + gap: 8px; + flex-shrink: 0; +} +.card-expand { + font-size: 12px; + opacity: 0.6; + transition: transform 150ms ease; + flex-shrink: 0; +} +.card-expand.is-expanded { + transform: rotate(90deg); +} + +.card-stats { + display: flex; + flex-direction: column; + gap: 6px; +} +.stat-row { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 6px; +} +.stat-label { + font-size: 10px; + text-transform: uppercase; + letter-spacing: 0.04em; + opacity: 0.6; + min-width: 96px; + flex-shrink: 0; +} +.card-stats :deep(.ant-tag) { + margin: 0; +} + +.card-clients { + margin-top: 4px; + padding-top: 8px; + border-top: 1px solid rgba(128, 128, 128, 0.15); +} + +.card-empty { + text-align: center; + opacity: 0.4; + padding: 20px 0; +} + @media (max-width: 768px) { - /* Card header/body breathe less on mobile */ :deep(.ant-card-head) { padding: 0 12px; min-height: 44px; } - :deep(.ant-card-head-title), :deep(.ant-card-extra) { padding: 8px 0; } - :deep(.ant-card-body) { padding: 8px; } - /* Filter bar wraps cleanly without forcing block layout (which made - * the input + radio group stack on separate full-width lines). */ .filter-bar.mobile { display: flex; flex-wrap: wrap; gap: 6px; } - .filter-bar.mobile > * { margin-bottom: 0; } - /* Tighten table cell padding so the 3 visible columns get room. */ - :deep(.ant-table-thead > tr > th), - :deep(.ant-table-tbody > tr > td) { - padding: 8px 6px; - font-size: 12px; - } - - /* Slightly bigger expand chevron (touch target). */ - :deep(.ant-table-row-expand-icon) { - width: 20px; - height: 20px; - line-height: 18px; - } - - /* The action / info icons are the row's primary touch targets. */ - .row-action-trigger, - .row-info-trigger { + .row-action-trigger { font-size: 22px; padding: 4px; } diff --git a/frontend/src/pages/inbounds/InboundsPage.vue b/frontend/src/pages/inbounds/InboundsPage.vue index a2b4f68f..b88fc7a5 100644 --- a/frontend/src/pages/inbounds/InboundsPage.vue +++ b/frontend/src/pages/inbounds/InboundsPage.vue @@ -549,12 +549,12 @@ function onRowAction({ key, dbInbound }) {
- + - + - + - + - + - +