"use client"; import { keepPreviousData } from "@tanstack/react-query"; import { usePathname } from "next/navigation"; import { useSession } from "next-auth/react"; import { useState, useEffect, useRef } from "react"; import dayjs from "@calcom/dayjs"; import { ColumnFilterType, ZDateRangeFilterValue, type FilterableColumn } from "@calcom/features/data-table"; import { DataTableProvider } from "~/data-table/DataTableProvider"; import { useFilterValue } from "~/data-table/hooks/useFilterValue"; import { useSegments } from "~/data-table/hooks/useSegments"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { trpc } from "@calcom/trpc/react"; import type { FilterType } from "@calcom/types/data-table"; import { Badge } from "@calcom/ui/components/badge"; import { Button } from "@calcom/ui/components/button"; import { PanelCard } from "@calcom/ui/components/card"; import { Icon } from "@calcom/ui/components/icon"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@calcom/ui/components/table/TableNew"; import { DateRangeFilter } from "~/data-table/components"; import { InvoicesTableSkeleton } from "./InvoicesTableSkeleton"; const DEFAULT_PAGE_SIZE = 10; const createdAtColumn: Extract }> = { id: "createdAt", title: "Date", type: ColumnFilterType.DATE_RANGE, }; function formatCurrency(amount: number, currency: string) { return new Intl.NumberFormat(undefined, { style: "currency", currency: currency.toUpperCase(), }).format(amount / 100); } function getStatusBadgeColor(status: string | null): "green" | "orange" | "gray" | "red" { switch (status) { case "paid": return "green"; case "open": return "orange"; case "draft": return "gray"; case "void": case "uncollectible": return "red"; default: return "gray"; } } export function InvoicesTable() { const pathname = usePathname(); if (!pathname) return null; return ( ); } function InvoicesTableContent() { const { t } = useLocale(); const pathname = usePathname(); const session = useSession(); const [cursors, setCursors] = useState<(string | null)[]>([null]); const [currentPage, setCurrentPage] = useState(0); const dateRangeFilter = useFilterValue("createdAt", ZDateRangeFilterValue)?.data; // Derive teamId from context (same logic as billing-view.tsx) const getTeamIdFromContext = () => { if (!pathname) return null; if (pathname.includes("/teams/") && pathname.includes("/billing")) { const teamIdMatch = pathname.match(/\/teams\/(\d+)\/billing/); return teamIdMatch ? parseInt(teamIdMatch[1], 10) : null; } if (pathname.includes("/organizations/billing")) { return session.data?.user?.org?.id ?? null; } return null; }; const teamId = getTeamIdFromContext(); const startDate = dateRangeFilter?.startDate ? new Date(dateRangeFilter.startDate) : undefined; const endDate = dateRangeFilter?.endDate ? new Date(dateRangeFilter.endDate) : undefined; const { data, isLoading, isFetching } = trpc.viewer.teams.listInvoices.useQuery( { teamId: teamId!, limit: DEFAULT_PAGE_SIZE, cursor: cursors[currentPage], startDate, endDate, }, { enabled: !!teamId, placeholderData: keepPreviousData } ); // Reset pagination when date filter changes const isFirstRender = useRef(true); useEffect(() => { if (isFirstRender.current) { isFirstRender.current = false; return; } setCursors([null]); setCurrentPage(0); }, [dateRangeFilter?.startDate, dateRangeFilter?.endDate]); if (!teamId) return null; if (isLoading) return ; const handleNextPage = () => { if (data?.nextCursor) { setCursors((prev) => [...prev, data.nextCursor]); setCurrentPage((prev) => prev + 1); } }; const handlePrevPage = () => { if (currentPage > 0) { setCurrentPage((prev) => prev - 1); } }; return ( } className="mt-5"> {!data?.invoices.length && currentPage === 0 ? (
{t("no_invoices_in_date_range")}
) : ( <>
{t("date")} {t("invoice_number")} {t("amount")} {t("status")} {t("description")} {t("actions")} {data?.invoices.map((invoice) => ( {dayjs.unix(invoice.created).format("MMM D, YYYY")} {invoice.number && invoice.hostedInvoiceUrl ? ( {invoice.number} ) : ( invoice.number || "-" )} {formatCurrency(invoice.amountPaid || invoice.amountDue, invoice.currency)} {invoice.status ? t(`invoice_status_${invoice.status}`) : "-"} {invoice.description || invoice.lineItems[0]?.description || "-"} {invoice.invoicePdf && (
{/* Pagination */} {(currentPage > 0 || data?.hasMore) && (
)} )}
); }