102 lines
3.5 KiB
TypeScript
102 lines
3.5 KiB
TypeScript
import { useTranslations, useFormatter } from "next-intl";
|
|
import { Card } from "@/components/ui/card";
|
|
import type { CreditNote } from "@/types";
|
|
|
|
interface Props {
|
|
creditNotes: CreditNote[];
|
|
}
|
|
|
|
const kindColors: Record<string, string> = {
|
|
// Voids = the invoice was cancelled; gentle red.
|
|
void: "text-error bg-error/10",
|
|
// Refunds = money returned; also red but slightly differentiated.
|
|
refund: "text-error bg-error/10",
|
|
};
|
|
|
|
/**
|
|
* Phase 7 — customer's credit-note history below the invoice list.
|
|
*
|
|
* Hidden entirely when the org has zero credit notes (most orgs in
|
|
* normal operation). When present, each row shows the credit-note
|
|
* number, the invoice it relates to, kind (void / refund), amount,
|
|
* and a download link to the PDF.
|
|
*
|
|
* No detail page — clicking the PDF link opens the document inline
|
|
* (browser PDF viewer), which IS the credit-note detail view. A
|
|
* separate per-credit-note web page would duplicate what's in the
|
|
* PDF and add no value.
|
|
*/
|
|
export function CustomerCreditNoteList({ creditNotes }: Props) {
|
|
const t = useTranslations("customerBilling");
|
|
const fmt = useFormatter();
|
|
|
|
if (creditNotes.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<Card>
|
|
<table className="w-full text-sm">
|
|
<thead className="text-xs text-text-muted text-left">
|
|
<tr>
|
|
<th className="pb-2 pr-4">{t("creditNoteNumberCol")}</th>
|
|
<th className="pb-2 pr-4">{t("creditNoteInvoiceCol")}</th>
|
|
<th className="pb-2 pr-4">{t("creditNoteIssuedCol")}</th>
|
|
<th className="pb-2 pr-4 text-right">{t("creditNoteAmountCol")}</th>
|
|
<th className="pb-2 pr-4 text-right">{t("creditNoteKindCol")}</th>
|
|
<th className="pb-2 text-right">{t("creditNotePdfCol")}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{creditNotes.map((cn) => (
|
|
<tr
|
|
key={cn.id}
|
|
className="border-t border-border align-middle"
|
|
>
|
|
<td className="py-2 pr-4 font-mono text-xs">
|
|
{cn.creditNoteNumber}
|
|
</td>
|
|
<td className="py-2 pr-4 font-mono text-xs text-text-secondary">
|
|
{cn.invoiceNumber}
|
|
</td>
|
|
<td className="py-2 pr-4 text-text-secondary whitespace-nowrap">
|
|
{fmt.dateTime(new Date(cn.issuedAt), { dateStyle: "medium" })}
|
|
</td>
|
|
<td className="py-2 pr-4 text-right font-mono whitespace-nowrap">
|
|
CHF {cn.amountChf.toFixed(2)}
|
|
</td>
|
|
<td className="py-2 pr-4 text-right">
|
|
<span
|
|
className={`px-2 py-0.5 rounded text-xs ${
|
|
kindColors[cn.kind] ?? ""
|
|
}`}
|
|
>
|
|
{t(`creditNoteKind_${cn.kind}` as any)}
|
|
</span>
|
|
</td>
|
|
<td className="py-2 text-right">
|
|
{cn.hasPdf ? (
|
|
<a
|
|
href={`/api/credit-notes/${encodeURIComponent(
|
|
cn.creditNoteNumber
|
|
)}/pdf`}
|
|
className="text-accent hover:underline text-xs"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
>
|
|
{t("downloadPdf")}
|
|
</a>
|
|
) : (
|
|
<span className="text-text-muted text-xs italic">
|
|
{t("creditNoteNoPdf")}
|
|
</span>
|
|
)}
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</Card>
|
|
);
|
|
}
|