fix(web): responsive billing table + filters on mobile
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled

The payments table forced min-w-[640px] inside overflow-x-auto, so mobile
got an awkward horizontal scroll, and the 5-option status filter could
overflow the viewport. Make the table a responsive layout: on mobile each
payment renders as a labelled stacked card (column label via ::before
from data-label, header row hidden), and on sm+ it stays a normal table.
Let the segmented filters wrap instead of overflowing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 00:36:03 +03:00
parent c633e846e2
commit cf1fee68a9

View File

@@ -46,7 +46,7 @@ function SegmentedFilter({
options: { value: string; label: string }[];
}) {
return (
<fieldset className="inline-flex items-center gap-1 rounded-lg bg-muted p-1">
<fieldset className="inline-flex max-w-full flex-wrap items-center gap-1 rounded-lg bg-muted p-1">
<legend className="sr-only">{label}</legend>
{options.map((option) => (
<button
@@ -67,6 +67,11 @@ function SegmentedFilter({
);
}
// Responsive cell: on mobile a labelled flex row (label via ::before from
// data-label); on sm+ a normal table cell.
const TD =
"flex items-center justify-between gap-4 py-1 before:font-medium before:text-muted-foreground before:content-[attr(data-label)] sm:table-cell sm:py-4 sm:before:content-none";
function BillingPage() {
const { t, locale } = useTranslation();
const [statusFilter, setStatusFilter] = useState<string>("all");
@@ -248,8 +253,8 @@ function BillingPage() {
</CardHeader>
<CardContent>
<div className="overflow-x-auto">
<table className="w-full min-w-[640px] text-sm">
<thead>
<table className="w-full text-sm">
<thead className="hidden sm:table-header-group">
<tr className="border-b text-left align-bottom text-muted-foreground">
<th scope="col" className="pb-3 font-medium">
{t("billing.date")}
@@ -273,28 +278,33 @@ function BillingPage() {
</thead>
<tbody className="divide-y">
{filteredPayments.slice(0, visibleCount).map((payment) => (
<tr key={payment.id}>
<td className="py-4 tabular-nums">
<tr key={payment.id} className="block py-3 sm:table-row sm:py-0">
<td data-label={t("billing.date")} className={`${TD} tabular-nums`}>
{new Date(payment.createdAt).toLocaleDateString(
locale === "tr" ? "tr-TR" : "en-US",
{ day: "2-digit", month: "2-digit", year: "numeric" },
)}
</td>
<td className="py-4">{payment.planName || "-"}</td>
<td className="py-4 text-right font-medium tabular-nums">
<td data-label={t("billing.plan")} className={TD}>
{payment.planName || "-"}
</td>
<td
data-label={t("billing.amount")}
className={`${TD} font-medium tabular-nums sm:text-right`}
>
{formatTRY(payment.amount)}
</td>
<td className="py-4 text-center">
<td data-label={t("billing.method")} className={`${TD} sm:text-center`}>
<Badge variant={payment.method === "stripe" ? "secondary" : "outline"}>
{t(`billing.methodLabels.${payment.method}`)}
</Badge>
</td>
<td className="py-4 text-center">
<td data-label={t("billing.status")} className={`${TD} sm:text-center`}>
<Badge variant={statusVariants[payment.status] || "secondary"}>
{t(`billing.statusLabels.${payment.status}`)}
</Badge>
</td>
<td className="py-4 text-right">
<td className="block pt-3 text-right sm:table-cell sm:py-4 sm:pt-4">
{payment.eftReceiptUrl ? (
<Button variant="ghost" size="sm" asChild>
<a