Skip to content

Invoice Page

Full Proforma Invoices page block with KPIs, activity chart, filterable table, and built-in Sheet form for CRUD.

Open in
Invoices/Proforma invoices
5 records

Proforma invoices

Wednesday, May 20, 2026

Total invoices
5

All PI records in the system

Pending approval
1

Awaiting Operation & Account sign-off

Total billed
AED 30,225.00

Sum of grant totals (AED)

VAT collected
AED 680.00

Total VAT across all invoices

Invoice activity

Last 30 days

Recent invoices

5 invoices · Draft, approval, and send workflow

PI NumberBill ToDateStatusApprovalGrant Total
KAT/2026/05/024Al Naboodah Contracting2026-05-20Pending approvalAED 14,385.00
KAT/2026/05/023Emaar Properties PJSC2026-05-19Sent to client
Mariam · Operation Department
Faisal · Account Department
AED 8,750.00
KAT/2026/05/022Sobha Realty Corporate Office2026-05-18Draft
KAT/2026/05/021Al Futtaim Carillion2026-05-17Approved
Nasser · Operation Department
Hind · Account Department
AED 4,190.00
KAT/2026/05/020Shapoorji Pallonji M.E.2026-05-16ExpiredAED 2,900.00
pnpm dlx shadcn@latest add @woxcn/manpowerhub-invoice

Install the ManpowerHub theme first, then add the block:

npx shadcn@latest add https://woxcn-registry.woxware.io/r/manpowerhub-invoice.json

This installs the full block at components/blocks/manpowerhub-invoice.tsx along with its component dependencies. Drop <InvoicePage /> inside an AppShellScrollBody for the full shell layout.

InvoicePage is a controlled component — it renders data you provide and calls handlers on user actions. Wire your own API calls or state management:

import { InvoicePage } from "@/components/blocks/manpowerhub-invoice";
export default function InvoicesRoute() {
const { data: invoices, mutate } = useInvoices(); // your data fetching
return (
<InvoicePage
invoices={invoices}
kpiTrend={kpiTrend}
onSaveInvoice={async (data, id) => {
if (id) {
await api.invoices.update(id, data);
} else {
await api.invoices.create(data);
}
mutate();
}}
onDeleteInvoice={async (id) => {
await api.invoices.delete(id);
mutate();
}}
onApproveInvoice={async (id, dept) => {
await api.invoices.approve(id, dept);
mutate();
}}
onRecallToDraft={async (id) => {
await api.invoices.recall(id);
mutate();
}}
onViewInvoice={(invoice) => router.push(`/invoices/${invoice.id}`)}
onDownloadInvoice={(invoice) => window.open(`/api/invoices/${invoice.id}/pdf`)}
/>
);
}

By default, InvoicePage ships with a built-in right-side Sheet form for create and edit. Override either with your own UI using the renderCreateSheet / renderEditSheet render props:

<InvoicePage
invoices={invoices}
onSaveInvoice={save}
onDeleteInvoice={del}
onApproveInvoice={approve}
onRecallToDraft={recall}
renderCreateSheet={({ open, onClose, onSave }) => (
<MyCustomInvoiceForm
open={open}
onClose={onClose}
onSubmit={onSave}
/>
)}
renderEditSheet={({ open, onClose, onSave, invoice }) => (
<MyCustomInvoiceForm
open={open}
onClose={onClose}
onSubmit={onSave}
defaultValues={invoice}
/>
)}
/>

The delete dialog cannot currently be overridden — open a GitHub issue if you need this.

PropTypeRequiredDescription
invoicesInvoice[]YesList of invoices to display
kpiTrendKpiTrendNoSparkline data for KPI cards. Defaults to flat if omitted.
onSaveInvoice(data, id?) => void | Promise<void>YesCalled on create (no id) and edit (with id).
onDeleteInvoice(id) => void | Promise<void>YesCalled after user confirms delete.
onApproveInvoice(id, dept) => void | Promise<void>YesCalled when approver signs off. dept is "Operation Department" or "Account Department".
onRecallToDraft(id) => void | Promise<void>YesCalled when user recalls an invoice to draft.
onViewInvoice(invoice) => voidNoCalled when user clicks View. If omitted, View is hidden.
onDownloadInvoice(invoice) => voidNoCalled when user clicks Download PDF. If omitted, Download is hidden.
renderCreateSheet(props) => ReactNodeNoOverride the built-in create Sheet form.
renderEditSheet(props) => ReactNodeNoOverride the built-in edit Sheet form.
interface LineItem {
id: string;
description: string;
hours: number;
rate: number;
}
interface ApprovalEntry {
name: string;
dept: "Operation Department" | "Account Department";
signed: boolean;
}
interface Invoice {
id: string;
piNumber: string;
billTo: string;
date: string;
status: "draft" | "pending" | "sent" | "approved" | "expired";
approvals: ApprovalEntry[];
lineItems: LineItem[];
grantTotal: number | null;
trn?: string;
project?: string;
vatApplicable: boolean;
}
interface InvoiceFormData {
piNumber: string;
billTo: string;
date: string;
approvals: ApprovalEntry[];
lineItems: LineItem[];
trn: string;
project: string;
vatApplicable: boolean;
}
interface KpiTrend {
total: number[];
pending: number[];
billed: number[];
vat: number[];
}