"use client"; import * as React from "react"; import { Button } from "~/components/ui/button"; import { Input } from "~/components/ui/input"; import { Label } from "~/components/ui/label"; import { DatePicker } from "~/components/ui/date-picker"; import { NumberInput } from "~/components/ui/number-input"; import { Trash2, Plus, GripVertical, ChevronUp, ChevronDown, } from "lucide-react"; import { cn } from "~/lib/utils"; interface InvoiceItem { id: string; date: Date; description: string; hours: number; rate: number; amount: number; } interface InvoiceLineItemsProps { items: InvoiceItem[]; onAddItem: () => void; onRemoveItem: (index: number) => void; onUpdateItem: ( index: number, field: string, value: string | number | Date, ) => void; onMoveUp: (index: number) => void; onMoveDown: (index: number) => void; className?: string; } interface LineItemRowProps { item: InvoiceItem; index: number; canRemove: boolean; onRemove: (index: number) => void; onUpdate: ( index: number, field: string, value: string | number | Date, ) => void; onMoveUp: (index: number) => void; onMoveDown: (index: number) => void; isFirst: boolean; isLast: boolean; } function LineItemRow({ item, index, canRemove, onRemove, onUpdate, }: LineItemRowProps) { return ( <> {/* Desktop Layout - Table Row */} {/* Drag Handle */} {/* Main Content */} {/* Description */}
onUpdate(index, "description", e.target.value)} placeholder="Describe the work performed..." className="w-full border-0 bg-transparent py-0 pr-0 pl-2 text-sm font-medium focus-visible:ring-0" />
{/* Controls Row */}
{/* Date */} onUpdate(index, "date", date ?? new Date()) } size="sm" className="h-9 w-28" /> {/* Hours */} onUpdate(index, "hours", value)} min={0} step={0.25} width="auto" className="h-9 w-28" /> {/* Rate */} onUpdate(index, "rate", value)} min={0} step={1} prefix="$" width="auto" className="h-9 w-28" /> {/* Amount */}
${(item.hours * item.rate).toFixed(2)}
{/* Actions */}
{/* Tablet Layout - Condensed Row */} {/* Drag Handle */} {/* Main Content - Description on top, inputs below */} {/* Description */}
onUpdate(index, "description", e.target.value)} placeholder="Describe the work performed..." className="w-full pl-3 text-sm font-medium" />
{/* Controls Row - Date/Hours/Rate break to separate rows on smaller screens */}
onUpdate(index, "date", date ?? new Date()) } size="sm" className="h-9 w-full sm:w-28" /> onUpdate(index, "hours", value)} min={0} step={0.25} width="full" className="h-9 w-1/2 sm:w-28" /> onUpdate(index, "rate", value)} min={0} step={1} prefix="$" width="full" className="h-9 w-1/2 sm:w-28" /> {/* Amount and Actions - inline with controls on larger screens */}
${(item.hours * item.rate).toFixed(2)}
); } function MobileLineItem({ item, index, canRemove, onRemove, onUpdate, onMoveUp, onMoveDown, isFirst, isLast, }: LineItemRowProps) { return (
{/* Description */}
onUpdate(index, "description", e.target.value)} placeholder="Describe the work performed..." className="pl-3 text-sm" />
{/* Date */}
onUpdate(index, "date", date ?? new Date())} size="sm" />
{/* Hours and Rate in a row */}
onUpdate(index, "hours", value)} min={0} step={0.25} width="full" />
onUpdate(index, "rate", value)} min={0} step={1} prefix="$" width="full" />
{/* Bottom section with controls, item name, and total */}
Item # {index + 1}
Total ${(item.hours * item.rate).toFixed(2)}
); } export function InvoiceLineItems({ items, onAddItem, onRemoveItem, onUpdateItem, onMoveUp, onMoveDown, className, }: InvoiceLineItemsProps) { const canRemoveItems = items.length > 1; return (
{/* Desktop and Tablet Table */}
{/* Desktop Header */} {/* Tablet Header */} {items.map((item, index) => ( ))}
Invoice Items
Invoice Items
{/* Mobile Cards */}
{items.map((item, index) => ( ))}
{/* Add Item Button */}
); }