Ship leftover Yimaru login/personas, sidebar sections, filter modal, chart/stat clip fixes, and matching feature docs so the polish work is fully committed. Co-authored-by: Cursor <cursoragent@cursor.com>
136 lines
4.8 KiB
TypeScript
136 lines
4.8 KiB
TypeScript
"use client";
|
|
|
|
import { useMemo, useState } from "react";
|
|
import { useTranslations } from "next-intl";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import { PageShell } from "@/components/layout/page-shell";
|
|
import { StatusBadge } from "@/components/shared/status-badge";
|
|
import { TableToolbar, matchesSearch } from "@/components/table/table-toolbar";
|
|
import { TablePanel } from "@/components/table/table-panel";
|
|
import { useAuth } from "@/providers/auth-provider";
|
|
import { mockApi } from "@/lib/api/mock-client";
|
|
import { formatDate, formatEtb } from "@/lib/format";
|
|
import { Link } from "@/i18n/routing";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from "@/components/ui/table";
|
|
import { ArrowLeft } from "lucide-react";
|
|
|
|
export default function MemberOrdersPage() {
|
|
const t = useTranslations();
|
|
const { user } = useAuth();
|
|
const memberId = user?.memberId ?? "mem_001";
|
|
const [search, setSearch] = useState("");
|
|
const [statusFilter, setStatusFilter] = useState("all");
|
|
|
|
const { data: orders, isLoading } = useQuery({
|
|
queryKey: ["member-orders", memberId],
|
|
queryFn: () => mockApi.getMemberOrders(memberId),
|
|
enabled: !!user,
|
|
});
|
|
|
|
const filtered = useMemo(() => {
|
|
return (orders ?? []).filter((order) => {
|
|
if (statusFilter !== "all" && order.status !== statusFilter) return false;
|
|
return matchesSearch([order.id, order.status], search);
|
|
});
|
|
}, [orders, search, statusFilter]);
|
|
|
|
if (!user) return null;
|
|
|
|
return (
|
|
<PageShell
|
|
title={t("member.ordersTitle")}
|
|
description={t("member.ordersSubtitle")}
|
|
breadcrumb
|
|
extra={
|
|
<Button variant="outline" className="gap-2" render={<Link href="/me" />}>
|
|
<ArrowLeft className="size-4" />
|
|
{t("common.back")}
|
|
</Button>
|
|
}
|
|
>
|
|
<TablePanel
|
|
loading={isLoading}
|
|
toolbar={
|
|
<TableToolbar
|
|
search={search}
|
|
onSearchChange={setSearch}
|
|
searchPlaceholder={t("common.searchPlaceholder")}
|
|
filters={[
|
|
{
|
|
id: "status",
|
|
label: t("common.status"),
|
|
value: statusFilter,
|
|
onChange: setStatusFilter,
|
|
allLabel: t("common.all"),
|
|
options: [
|
|
{ value: "placed", label: "Placed" },
|
|
{ value: "processing", label: "Processing" },
|
|
{ value: "delivered", label: "Delivered" },
|
|
{ value: "cancelled", label: "Cancelled" },
|
|
],
|
|
},
|
|
]}
|
|
resultCount={filtered.length}
|
|
resultLabel={t("common.results")}
|
|
/>
|
|
}
|
|
>
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead>{t("member.order")}</TableHead>
|
|
<TableHead className="text-right">{t("statements.total")}</TableHead>
|
|
<TableHead className="text-right">{t("statements.covered")}</TableHead>
|
|
<TableHead className="text-right">{t("member.memberPaid")}</TableHead>
|
|
<TableHead>{t("member.items")}</TableHead>
|
|
<TableHead>{t("common.status")}</TableHead>
|
|
<TableHead>{t("member.placed")}</TableHead>
|
|
<TableHead>{t("common.actions")}</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{filtered.length ? (
|
|
filtered.map((order) => (
|
|
<TableRow key={order.id}>
|
|
<TableCell className="font-mono text-xs">{order.id}</TableCell>
|
|
<TableCell className="text-right">{formatEtb(order.totalEtb)}</TableCell>
|
|
<TableCell className="text-right">{formatEtb(order.coveredEtb)}</TableCell>
|
|
<TableCell className="text-right">{formatEtb(order.memberPaidEtb)}</TableCell>
|
|
<TableCell>{order.itemCount}</TableCell>
|
|
<TableCell>
|
|
<StatusBadge status={order.status} />
|
|
</TableCell>
|
|
<TableCell>{formatDate(order.placedAt)}</TableCell>
|
|
<TableCell>
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
render={<Link href={`/me/orders/${order.id}`} />}
|
|
>
|
|
{t("common.view")}
|
|
</Button>
|
|
</TableCell>
|
|
</TableRow>
|
|
))
|
|
) : (
|
|
<TableRow>
|
|
<TableCell colSpan={8} className="h-28 text-center text-sm text-muted-foreground">
|
|
{t("common.empty")}
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
</TablePanel>
|
|
</PageShell>
|
|
);
|
|
}
|