mirror of
https://github.com/tiennm99/exchange-rate-export.git
synced 2026-10-14 10:18:25 +00:00
feat: add responsive card layout for tables on mobile screens
Tables show as cards on small screens (<640px) with key rate fields in a compact grid, while desktop retains the full table view.
This commit is contained in:
1 parent
e235aaff6d
commit
8bd463227b
1 file changed
+135
-78
@@ -13,49 +13,69 @@ export function ComparisonTable({ results }) {
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="overflow-x-auto rounded-lg border border-[var(--card-border)]">
|
||||
<table className="min-w-full text-sm">
|
||||
<thead className="sticky top-0 z-10">
|
||||
<tr className="bg-[var(--table-header-bg)]">
|
||||
{headers.map((h) => (
|
||||
<th
|
||||
key={h.label}
|
||||
className={`px-3 py-2.5 font-semibold whitespace-nowrap border-b border-[var(--card-border)] ${h.numeric ? "text-right" : "text-left"}`}
|
||||
>
|
||||
{h.label}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{results.map((row, idx) => {
|
||||
const cells = [
|
||||
{ value: row.date, numeric: false },
|
||||
{ value: row.currency, numeric: false },
|
||||
{ value: row.bidvBuyTm, numeric: true },
|
||||
{ value: row.bidvSell, numeric: true },
|
||||
{ value: row.tcbBidTm, numeric: true },
|
||||
{ value: row.tcbAsk, numeric: true },
|
||||
];
|
||||
return (
|
||||
<tr
|
||||
key={idx}
|
||||
className={`transition-colors hover:!bg-[var(--table-row-hover)] ${idx % 2 !== 0 ? "bg-[var(--table-row-stripe)]" : ""}`}
|
||||
>
|
||||
{cells.map((cell, i) => (
|
||||
<td
|
||||
key={i}
|
||||
className={`px-3 py-2 whitespace-nowrap border-b border-[var(--card-border)] ${cell.numeric ? "text-right tabular-nums" : ""}`}
|
||||
>
|
||||
{cell.value || "-"}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<>
|
||||
{/* Desktop table */}
|
||||
<div className="hidden sm:block overflow-x-auto rounded-lg border border-[var(--card-border)]">
|
||||
<table className="min-w-full text-sm">
|
||||
<thead className="sticky top-0 z-10">
|
||||
<tr className="bg-[var(--table-header-bg)]">
|
||||
{headers.map((h) => (
|
||||
<th
|
||||
key={h.label}
|
||||
className={`px-3 py-2.5 font-semibold whitespace-nowrap border-b border-[var(--card-border)] ${h.numeric ? "text-right" : "text-left"}`}
|
||||
>
|
||||
{h.label}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{results.map((row, idx) => {
|
||||
const cells = [
|
||||
{ value: row.date, numeric: false },
|
||||
{ value: row.currency, numeric: false },
|
||||
{ value: row.bidvBuyTm, numeric: true },
|
||||
{ value: row.bidvSell, numeric: true },
|
||||
{ value: row.tcbBidTm, numeric: true },
|
||||
{ value: row.tcbAsk, numeric: true },
|
||||
];
|
||||
return (
|
||||
<tr
|
||||
key={idx}
|
||||
className={`transition-colors hover:!bg-[var(--table-row-hover)] ${idx % 2 !== 0 ? "bg-[var(--table-row-stripe)]" : ""}`}
|
||||
>
|
||||
{cells.map((cell, i) => (
|
||||
<td
|
||||
key={i}
|
||||
className={`px-3 py-2 whitespace-nowrap border-b border-[var(--card-border)] ${cell.numeric ? "text-right tabular-nums" : ""}`}
|
||||
>
|
||||
{cell.value || "-"}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/* Mobile cards */}
|
||||
<div className="sm:hidden flex flex-col gap-2">
|
||||
{results.map((row, idx) => (
|
||||
<div key={idx} className="rounded-lg border border-[var(--card-border)] p-3 bg-[var(--card-bg)] text-sm">
|
||||
<div className="flex justify-between mb-1.5">
|
||||
<span className="font-medium">{row.date}</span>
|
||||
<span className="text-[var(--muted)]">{row.currency}</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-x-4 gap-y-1 text-xs">
|
||||
<div className="flex justify-between"><span className="text-[var(--muted)]">BIDV Buy</span><span className="tabular-nums">{row.bidvBuyTm || "-"}</span></div>
|
||||
<div className="flex justify-between"><span className="text-[var(--muted)]">TCB Bid</span><span className="tabular-nums">{row.tcbBidTm || "-"}</span></div>
|
||||
<div className="flex justify-between"><span className="text-[var(--muted)]">BIDV Sell</span><span className="tabular-nums">{row.bidvSell || "-"}</span></div>
|
||||
<div className="flex justify-between"><span className="text-[var(--muted)]">TCB Ask</span><span className="tabular-nums">{row.tcbAsk || "-"}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -106,42 +126,79 @@ export function RateTable({ results, selectedBank }) {
|
||||
];
|
||||
};
|
||||
|
||||
const getMobileCard = (row) => {
|
||||
if (selectedBank === "bidv") {
|
||||
return { title: row.nameVI || row.nameEN, subtitle: `${row.currency} - ${row.date}`, fields: [
|
||||
{ label: "Buy TM", value: row.muaTm }, { label: "Buy CK", value: row.muaCk }, { label: "Sell", value: row.ban },
|
||||
]};
|
||||
}
|
||||
return { title: row.label, subtitle: `${row.sourceCurrency} - ${row.date}`, fields: [
|
||||
{ label: "Bid TM", value: row.bidRateTM }, { label: "Bid CK", value: row.bidRateCK },
|
||||
{ label: "Ask", value: row.askRate }, { label: "Ask TM", value: row.askRateTM },
|
||||
]};
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="overflow-x-auto rounded-lg border border-[var(--card-border)]">
|
||||
<table className="min-w-full text-sm">
|
||||
<thead className="sticky top-0 z-10">
|
||||
<tr className="bg-[var(--table-header-bg)]">
|
||||
{headers.map((h) => (
|
||||
<th
|
||||
key={h.label}
|
||||
className={`px-3 py-2.5 font-semibold whitespace-nowrap border-b border-[var(--card-border)] ${h.numeric ? "text-right" : "text-left"}`}
|
||||
>
|
||||
{h.label}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{results.map((row, idx) => {
|
||||
const cells = getRowCells(row);
|
||||
return (
|
||||
<tr
|
||||
key={idx}
|
||||
className={`transition-colors hover:!bg-[var(--table-row-hover)] ${idx % 2 !== 0 ? "bg-[var(--table-row-stripe)]" : ""}`}
|
||||
>
|
||||
{cells.map((cell, i) => (
|
||||
<td
|
||||
key={i}
|
||||
className={`px-3 py-2 whitespace-nowrap border-b border-[var(--card-border)] ${cell.numeric ? "text-right tabular-nums" : ""}`}
|
||||
>
|
||||
{cell.value}
|
||||
</td>
|
||||
<>
|
||||
{/* Desktop table */}
|
||||
<div className="hidden sm:block overflow-x-auto rounded-lg border border-[var(--card-border)]">
|
||||
<table className="min-w-full text-sm">
|
||||
<thead className="sticky top-0 z-10">
|
||||
<tr className="bg-[var(--table-header-bg)]">
|
||||
{headers.map((h) => (
|
||||
<th
|
||||
key={h.label}
|
||||
className={`px-3 py-2.5 font-semibold whitespace-nowrap border-b border-[var(--card-border)] ${h.numeric ? "text-right" : "text-left"}`}
|
||||
>
|
||||
{h.label}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{results.map((row, idx) => {
|
||||
const cells = getRowCells(row);
|
||||
return (
|
||||
<tr
|
||||
key={idx}
|
||||
className={`transition-colors hover:!bg-[var(--table-row-hover)] ${idx % 2 !== 0 ? "bg-[var(--table-row-stripe)]" : ""}`}
|
||||
>
|
||||
{cells.map((cell, i) => (
|
||||
<td
|
||||
key={i}
|
||||
className={`px-3 py-2 whitespace-nowrap border-b border-[var(--card-border)] ${cell.numeric ? "text-right tabular-nums" : ""}`}
|
||||
>
|
||||
{cell.value}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{/* Mobile cards */}
|
||||
<div className="sm:hidden flex flex-col gap-2">
|
||||
{results.map((row, idx) => {
|
||||
const card = getMobileCard(row);
|
||||
return (
|
||||
<div key={idx} className="rounded-lg border border-[var(--card-border)] p-3 bg-[var(--card-bg)] text-sm">
|
||||
<div className="flex justify-between mb-1.5">
|
||||
<span className="font-medium">{card.title}</span>
|
||||
<span className="text-[var(--muted)] text-xs">{card.subtitle}</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-x-4 gap-y-1 text-xs">
|
||||
{card.fields.map((f, i) => (
|
||||
<div key={i} className="flex justify-between">
|
||||
<span className="text-[var(--muted)]">{f.label}</span>
|
||||
<span className="tabular-nums">{f.value || "-"}</span>
|
||||
</div>
|
||||
))}
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user