2cfbbec9dc
Lint went from 84 problems to 0. Build passes. Dead code: - TeamInvoices: drop the orphaned Subcontractor PO block (updateSubcontractorPO plus send/ready-to-pay/paid/reopen/cancel/delete/download/CPA-export handlers and ~25 derived vars). That feature moved to the routed TeamCreateSubcontractorPO / TeamSubcontractorPODetail pages in the Layout2 redesign; these were leftovers. 2100 -> 1880 lines. - Tasks: drop unrendered project sort/tab/completion logic in TasksPageShell. - Remove 59 unused vars, imports, and handlers across 12 other files. Removed wasted queries: - TeamInvoices ran a nested subcontractor_payments join (profiles, projects, companies, PO items, tasks) on every page load into state nothing read. - TeamInvoices and ExternalMyInvoices each fetched a task-type map on invoice open and discarded it; the ExternalMyInvoices one blocked the detail popup. - RequestForm queried sign_families on mount into unread state. Dependencies: - Drop heic2any (zero imports). heic-to is the one in use and already lazy-loads. Effects and exports: - RequestForm: remove 4 effects. Sign-row sizing and company-change resets move into their change handlers; single-company selection is derived during render rather than written back into form state. - FilterDropdown: measure position on click instead of in an effect, which also removes a one-frame stale-position flicker. - BrandBook: latest-ref pattern so the 900ms address debounce is not reset by handler identity. - ClientMyInvoices: hoist MONTHS to module scope. - Split non-component exports: POPUP_FIELD_LABEL moves to lib/popupStyles.js; resolveProfileAvatar and useLiveClock are internal-only; getGreeting deleted as nothing referenced it. Companies.jsx keeps one scoped eslint-disable with a reason: load() only setStates after awaiting its queries, so set-state-in-effect is a false positive there, and load() is shared with the create/delete handlers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
67 lines
2.6 KiB
React
67 lines
2.6 KiB
React
import { popupOverlayStyle, popupSurfaceStyle } from '../lib/popupStyles';
|
|
import PageLoader from './PageLoader';
|
|
|
|
export default function InvoiceDetailPopup({
|
|
title,
|
|
subtitle,
|
|
headerRight,
|
|
metaContent, // JSX fields rendered in flat meta grid (no cards)
|
|
metaActions, // optional JSX rendered right-aligned beside meta grid (e.g. Edit Dates)
|
|
metaCols = 4, // number of grid columns for meta strip
|
|
footerActions,
|
|
loading = false,
|
|
children,
|
|
}) {
|
|
return (
|
|
<div style={popupOverlayStyle}>
|
|
<div
|
|
style={{ ...popupSurfaceStyle, width: '80vw', height: '80vh', display: 'flex', flexDirection: 'column', gap: 0 }}
|
|
onClick={e => e.stopPropagation()}
|
|
>
|
|
{/* Header */}
|
|
<div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 16, paddingBottom: 18, borderBottom: '1px solid var(--border)', flexShrink: 0 }}>
|
|
<div>
|
|
<div style={{ fontSize: 18, fontWeight: 500, color: 'var(--text-primary)', letterSpacing: 0.2, lineHeight: 1.1 }}>{title}</div>
|
|
{subtitle && <div style={{ marginTop: 6, fontSize: 13, color: 'var(--text-secondary)' }}>{subtitle}</div>}
|
|
</div>
|
|
{headerRight && <div style={{ flexShrink: 0 }}>{headerRight}</div>}
|
|
</div>
|
|
|
|
{/* Flat meta strip */}
|
|
{metaContent && (
|
|
<div style={{ padding: '18px 0', borderBottom: '1px solid var(--border)', flexShrink: 0 }}>
|
|
<div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 16 }}>
|
|
<div style={{ display: 'grid', gridTemplateColumns: `repeat(${metaCols}, minmax(0, 1fr))`, gap: '14px 18px', flex: 1 }}>
|
|
{metaContent}
|
|
</div>
|
|
{metaActions && (
|
|
<div style={{ flexShrink: 0, display: 'flex', gap: 8, alignItems: 'flex-start' }}>
|
|
{metaActions}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Body */}
|
|
{loading ? (
|
|
<div style={{ flex: 1, minHeight: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
<PageLoader />
|
|
</div>
|
|
) : (
|
|
<div className="scrollbar-thin-theme table-scroll-hidden" style={{ flex: 1, minHeight: 0, overflowY: 'auto', paddingTop: 18 }}>
|
|
{children}
|
|
</div>
|
|
)}
|
|
|
|
{/* Footer */}
|
|
{footerActions && (
|
|
<div className="modal-action-row" style={{ paddingTop: 18, borderTop: '1px solid var(--border)', flexShrink: 0 }}>
|
|
{footerActions}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|