WFDASHDRIVER-APP/components/PoActions.tsx

141 lines
4.9 KiB
TypeScript
Raw Normal View History

'use client';
import { useRef, useState } from 'react';
import { Camera, FileText, Loader2, Check } from 'lucide-react';
import { OrderItem, PurchaseOrder } from '@/lib/types';
import { buildPoFileUrl, countInvoicePages, invoicePhotoTarget } from '@/lib/poDocs';
import InvoiceCheck from '@/components/InvoiceCheck';
import { cn } from '@/lib/utils';
interface PoActionsProps {
purchaseOrder: PurchaseOrder | null;
items: OrderItem[];
/** Uploads one invoice page for this PO. Rejects on failure. */
onAddInvoicePage: (items: OrderItem[], file: File) => Promise<void>;
/** False hides the photo button (e.g. the caller can't upload). */
canPhoto?: boolean;
/** 'lg' = full-width stacked buttons, for the PO bottom sheet. */
size?: 'sm' | 'lg';
className?: string;
}
// The two PO-level buttons on a PO header: open the PO PDF in one tap,
// and photograph the supplier invoice once for the whole PO (with
// "Page 2", "Page 3"… for multi-page invoices).
export default function PoActions({
purchaseOrder,
items,
onAddInvoicePage,
canPhoto: photoAllowed = true,
size = 'sm',
className,
}: PoActionsProps) {
const inputRef = useRef<HTMLInputElement>(null);
const [uploading, setUploading] = useState(false);
const pdfUrl = buildPoFileUrl(purchaseOrder);
// A customer PO's photo goes on one of its lines; a stock order's on
// the stock purchase. No target (no PO at all) means no photo button.
const canPhoto = photoAllowed && invoicePhotoTarget(items) !== null;
const pages = countInvoicePages(items);
if (!pdfUrl && !canPhoto) return null;
const handleFile = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
// Reset so taking the same shot again still fires onChange.
e.target.value = '';
if (!file || uploading) return;
setUploading(true);
try {
await onAddInvoicePage(items, file);
} catch {
// The page handler already told the driver; nothing to add here.
} finally {
setUploading(false);
}
};
const poLabel = purchaseOrder?.po_number ? `PO ${purchaseOrder.po_number}` : 'this PO';
const lg = size === 'lg';
const btn = lg
? 'h-12 w-full rounded-xl text-[15px] font-semibold flex items-center justify-center gap-2 transition-colors touch-manipulation'
: 'h-9 px-3 rounded-lg text-[13px] font-semibold flex items-center gap-1.5 transition-colors touch-manipulation';
const icon = lg ? 18 : 14;
return (
<div className={cn(lg ? 'flex flex-col gap-2' : 'flex items-center gap-1 flex-shrink-0', className)}>
{pdfUrl && (
<a
href={pdfUrl}
target="_blank"
rel="noopener noreferrer"
aria-label={`Open the PDF for ${poLabel}`}
className={cn(
btn,
lg ? 'bg-[#26251e] text-white active:bg-black' : 'bg-[#efeaf7] text-[#6c5a99] active:bg-[#ded5ef]',
)}
>
<FileText size={icon} aria-hidden="true" />
<span>{lg ? 'Open PO PDF' : 'PO'}</span>
</a>
)}
{canPhoto && (
<>
<button
type="button"
onClick={() => inputRef.current?.click()}
disabled={uploading}
aria-busy={uploading}
aria-label={
pages === 0
? `Take a photo of the invoice for ${poLabel}`
: `Add page ${pages + 1} of the invoice for ${poLabel}`
}
className={cn(
btn,
pages > 0
? 'bg-[#e6f1ec] text-[#125a3e] active:bg-[#cfe6db]'
: lg
? 'bg-white text-[#26251e] border border-[rgba(38,37,30,0.2)] active:bg-[#f2f1ed]'
: 'bg-[#26251e] text-white active:bg-black',
uploading && 'opacity-60',
)}
>
{uploading ? (
<Loader2 size={icon} className="animate-spin" aria-hidden="true" />
) : pages === 0 ? (
<Camera size={icon} aria-hidden="true" />
) : (
<Check size={icon} aria-hidden="true" />
)}
<span>
{uploading
? 'Uploading…'
: pages === 0
? lg ? 'Invoice photo' : 'Invoice'
: lg
? `Invoice: ${pages} page${pages === 1 ? '' : 's'} · add page ${pages + 1}`
: `${pages} pg · add pg ${pages + 1}`}
</span>
</button>
<input
ref={inputRef}
type="file"
accept="image/*"
capture="environment"
onChange={handleFile}
className="hidden"
aria-hidden="true"
tabIndex={-1}
/>
</>
)}
{/* Beta helper: renders nothing unless switched on in Settings. */}
{purchaseOrder?.po_number && (
<InvoiceCheck poNumber={purchaseOrder.po_number} pages={pages} size={size} />
)}
</div>
);
}