From ea25c1159184ed8eabd82bc2a0ff1afe3963186f Mon Sep 17 00:00:00 2001 From: waterWang Date: Wed, 29 Jul 2026 18:53:52 +0800 Subject: [PATCH] feat: improve mobile responsiveness of admin dashboard tables (Closes #235) --- app/[communitySlug]/admin/page.tsx | 19 +++++----- app/analytics/page.tsx | 19 +++++----- app/globals.css | 60 ++++++++++++++++++++++++++++++ components/ui/responsive-table.tsx | 35 +++++++++++++++++ 4 files changed, 115 insertions(+), 18 deletions(-) create mode 100644 components/ui/responsive-table.tsx diff --git a/app/[communitySlug]/admin/page.tsx b/app/[communitySlug]/admin/page.tsx index 80c5bd5..e24474f 100644 --- a/app/[communitySlug]/admin/page.tsx +++ b/app/[communitySlug]/admin/page.tsx @@ -8,6 +8,7 @@ import { useParams } from 'next/navigation'; import { config } from '@/lib/config'; import { isApiError } from '@/lib/api/errors'; import { queryKeys } from '@/lib/query'; +import { ResponsiveTable } from '@/components/ui/responsive-table'; import { EmptyState, ErrorState, LoadingState, safeErrorMessage } from '@/components/ui/api-states'; import { AddressText } from '@/components/wallet/address-text'; import { AdminGuard } from '@/components/admin-guard'; @@ -284,7 +285,7 @@ function WebhookLogsContent() { /> ) : (
-
+ @@ -304,17 +305,17 @@ function WebhookLogsContent() { className="hover:bg-muted/50 transition-colors cursor-pointer" onClick={() => toggleExpand(evt.id)} > - - - - - - {isMockMode && ( -
+ + {new Date(evt.timestamp).toLocaleString()} +
{evt.eventType} {evt.isReplay && ( @@ -324,7 +325,7 @@ function WebhookLogsContent() { )}
+ + + {JSON.stringify(evt.payloadSummary)} +
-
+
)} diff --git a/app/analytics/page.tsx b/app/analytics/page.tsx index 5c185b9..eb406c5 100644 --- a/app/analytics/page.tsx +++ b/app/analytics/page.tsx @@ -5,6 +5,7 @@ import { Nav } from "@/components/nav"; import { PortfolioChart, type Timeframe } from "@/components/analytics/PortfolioChart"; import { AssetBreakdown } from "@/components/analytics/AssetBreakdown"; import { YieldPerformanceSummary } from "@/components/analytics/YieldPerformanceSummary"; +import { ResponsiveTable } from '@/components/ui/responsive-table'; import { BarChart3, TrendingUp, @@ -215,7 +216,7 @@ export default function AnalyticsPage() { No active strategies found for this account. ) : ( -
+ @@ -231,7 +232,7 @@ export default function AnalyticsPage() { {pools.map((pool) => ( - - - + - - - -
+ {pool.name} {pool.status === "Boosting" && ( @@ -239,20 +240,20 @@ export default function AnalyticsPage() { )} {pool.category} + {pool.category} {formatCurrency(pool.stakedBalance)} + {pool.apy}% + +{formatCurrency(pool.return30D)} + {formatCurrency(pool.unclaimedYield)} +
-
+ )} diff --git a/app/globals.css b/app/globals.css index f06f56b..9302c48 100644 --- a/app/globals.css +++ b/app/globals.css @@ -66,3 +66,63 @@ body { } } } + +/* ── Responsive table ──────────────────────────────────────────────────── */ +/* On screens narrower than sm (640px), each row becomes a card. */ +.responsive-table-wrapper table { + width: 100%; + border-collapse: collapse; +} + +@media (max-width: 639px) { + .responsive-table-wrapper { + display: block; + } + + .responsive-table-wrapper thead { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; + } + + .responsive-table-wrapper tbody, + .responsive-table-wrapper tr, + .responsive-table-wrapper td { + display: block; + } + + .responsive-table-wrapper tr { + margin-bottom: 1rem; + border: 1px solid hsl(var(--border)); + border-radius: 0.5rem; + padding: 0.75rem; + background-color: hsl(var(--card)); + } + + .responsive-table-wrapper td { + padding: 0.375rem 0; + border: none !important; + text-align: left !important; + } + + .responsive-table-wrapper td::before { + content: attr(data-label); + display: block; + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + color: hsl(var(--muted-foreground)); + margin-bottom: 0.125rem; + } + + .responsive-table-wrapper td[data-label=""]::before { + display: none; + } +} diff --git a/components/ui/responsive-table.tsx b/components/ui/responsive-table.tsx new file mode 100644 index 0000000..50d7080 --- /dev/null +++ b/components/ui/responsive-table.tsx @@ -0,0 +1,35 @@ +"use client"; + +import React from "react"; + +/** + * A responsive table wrapper that renders as a normal table on desktop + * (≥sm breakpoint) and transforms to a stacked card layout on mobile. + * + * Usage: wrap your existing `` with this component. + * Each `
` should have a `data-label` attribute whose value is the + * column name — it will be shown as a label on mobile. + * + * @example + * ```tsx + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
NameRole
AliceAdmin
+ *
+ * ``` + */ +export function ResponsiveTable({ children }: { children: React.ReactNode }) { + return
{children}
; +} \ No newline at end of file