fix: remove cards from invoice popups, match original flat layout

Replace SubcontractorInvoiceDetailView (card-based) with flat layout
matching the original sub-invoice popup: meta strip + raw table + notes.

- InvoiceDetailPopup: add metaContent/metaActions/metaCols props + export POPUP_FIELD_LABEL
- New InvoicePopupTable: flat sortable 5-col table, no card wrapper
- All 4 popups (client, team invoice, team sub-invoice, external): use
  flat meta strip (4-col grid, borderBottom) and InvoicePopupTable
- Sub-invoice + external: Submitted | Paid/Created | Items | Total
- Client invoice: Date | Due | Status | Total
- Team invoice: Date | Due | Company | Email | Total (+ stripe if paid)
  with Edit Dates button in metaActions slot

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Krao Hasanee
2026-06-08 14:01:59 -04:00
parent 6e4e99c33c
commit 0945b548c1
6 changed files with 191 additions and 151 deletions
+13 -30
View File
@@ -9,8 +9,8 @@ import { readPageCache, writePageCache } from '../../lib/pageCache';
import { useSortable } from '../../hooks/useSortable';
import { isCompletedVersionEligible } from '../../lib/invoiceVersionRules';
import SubcontractorInvoiceForm from '../../components/SubcontractorInvoiceForm';
import InvoiceDetailPopup from '../../components/InvoiceDetailPopup';
import SubcontractorInvoiceDetailView from '../../components/SubcontractorInvoiceDetailView';
import InvoiceDetailPopup, { POPUP_FIELD_LABEL } from '../../components/InvoiceDetailPopup';
import InvoicePopupTable from '../../components/InvoicePopupTable';
import { generateSubcontractorPOPDF } from '../../lib/invoice';
const STATUS_BADGE = { draft: 'not_started', submitted: 'in_progress', paid: 'client_approved' };
@@ -462,6 +462,7 @@ export default function MyInvoices() {
if (key === 'line_total') return Number(item.unit_price || 0) * Number(item.quantity || 1);
return '';
});
const F = POPUP_FIELD_LABEL;
return (
<InvoiceDetailPopup
title={inv?.invoice_number || 'Subcontractor Invoice'}
@@ -470,6 +471,12 @@ export default function MyInvoices() {
onClose={() => { if (!busy) { setViewingInvoice(null); setDetailError(''); } }}
blockClose={busy}
loading={detailLoading && !inv}
metaContent={inv ? <>
<div><div style={F}>Created</div><div style={{ fontSize: 13, color: 'var(--text-primary)' }}>{inv.created_at ? new Date(inv.created_at).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) : '—'}</div></div>
<div><div style={F}>Submitted</div><div style={{ fontSize: 13, color: 'var(--text-primary)' }}>{inv.submitted_at ? new Date(inv.submitted_at).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) : '—'}</div></div>
<div><div style={F}>Line Items</div><div style={{ fontSize: 13, color: 'var(--text-primary)' }}>{baseItems.length}</div></div>
<div><div style={F}>Total</div><div style={{ fontSize: 18, fontWeight: 500, color: 'var(--accent)', lineHeight: 1.1 }}>${total.toFixed(2)}</div></div>
</> : null}
footerActions={inv ? (
<>
<LoadingButton className="btn btn-outline" loading={downloadingInvoice} loadingText="Generating…" disabled={busy} onClick={handleDownloadInvoice}>Download Invoice</LoadingButton>
@@ -480,34 +487,10 @@ export default function MyInvoices() {
) : null}
>
{detailError && <div className="notification notification-info" style={{ marginBottom: 16 }}>{detailError}</div>}
{inv ? (
<SubcontractorInvoiceDetailView
leftCardTitle="Submitted By"
leftCardBody={<>
<div style={{ fontSize: 15, fontWeight: 400 }}>{currentUser?.name || 'Subcontractor'}</div>
<div style={{ fontSize: 13, color: 'var(--text-muted)', marginTop: 4 }}>{currentUser?.email || '—'}</div>
</>}
rightCardTitle="Invoice Details"
rightCardBody={
<div className="invoice-detail-meta-grid">
<div className="invoice-detail-meta-item"><label>Invoice #</label><p>{inv.invoice_number || '—'}</p></div>
<div className="invoice-detail-meta-item"><label>Status</label><p>{invoiceStatus}</p></div>
<div className="invoice-detail-meta-item"><label>Created</label><p>{inv.created_at ? new Date(inv.created_at).toLocaleDateString() : '—'}</p></div>
<div className="invoice-detail-meta-item"><label>Submitted</label><p>{inv.submitted_at ? new Date(inv.submitted_at).toLocaleDateString() : '—'}</p></div>
<div className="invoice-detail-meta-item"><label>Line Items</label><p>{baseItems.length}</p></div>
<div className="invoice-detail-meta-item"><label>Total</label><p style={{ fontSize: 18, color: 'var(--accent)' }}>${total.toFixed(2)}</p></div>
</div>
}
sortKey={detailSortKey}
sortDir={detailSortDir}
onSort={toggleDetailSort}
items={tableItems}
notes={inv.notes}
total={total}
/>
) : !detailError ? (
<div className="card-empty-center">Invoice not found.</div>
) : null}
{inv
? <InvoicePopupTable sortKey={detailSortKey} sortDir={detailSortDir} onSort={toggleDetailSort} items={tableItems} notes={inv.notes} />
: !detailError ? <div className="card-empty-center">Invoice not found.</div> : null
}
</InvoiceDetailPopup>
);
})()}