feat: customer lookup from POS with order history and item search

- Customer dialog in cart panel: search accounts by name, phone, email, account #
- Selected customer shown with name, phone, email in cart header
- accountId passed when creating transactions
- Order history view: tap a transaction to expand and see line items
- Item search in history (e.g. "strings") — filters orders containing that item
- Backend: add accountId and itemSearch filters to transaction list endpoint
- itemSearch uses EXISTS subquery on line item descriptions (ILIKE)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
ryan
2026-04-04 21:07:24 +00:00
parent cf299ac1d2
commit d21972212b
7 changed files with 310 additions and 5 deletions

View File

@@ -3,10 +3,11 @@ import { usePOSStore } from '@/stores/pos.store'
import { posMutations, posKeys, type Transaction } from '@/api/pos'
import { Button } from '@/components/ui/button'
import { Separator } from '@/components/ui/separator'
import { X, Banknote, CreditCard, FileText, Ban } from 'lucide-react'
import { X, Banknote, CreditCard, FileText, Ban, UserRound } from 'lucide-react'
import { toast } from 'sonner'
import { useState } from 'react'
import { POSPaymentDialog } from './pos-payment-dialog'
import { POSCustomerDialog } from './pos-customer-dialog'
interface POSCartPanelProps {
transaction: Transaction | null
@@ -14,8 +15,9 @@ interface POSCartPanelProps {
export function POSCartPanel({ transaction }: POSCartPanelProps) {
const queryClient = useQueryClient()
const { currentTransactionId, setTransaction } = usePOSStore()
const { currentTransactionId, setTransaction, accountName, accountPhone, accountEmail } = usePOSStore()
const [paymentMethod, setPaymentMethod] = useState<string | null>(null)
const [customerOpen, setCustomerOpen] = useState(false)
const lineItems = transaction?.lineItems ?? []
const drawerSessionId = usePOSStore((s) => s.drawerSessionId)
@@ -63,6 +65,24 @@ export function POSCartPanel({ transaction }: POSCartPanelProps) {
</span>
)}
</div>
<button
onClick={() => setCustomerOpen(true)}
className="flex items-start gap-1.5 mt-1 text-xs text-muted-foreground hover:text-foreground text-left"
>
<UserRound className="h-3 w-3 mt-0.5 shrink-0" />
{accountName ? (
<span>
<span className="font-medium text-foreground">{accountName}</span>
{(accountPhone || accountEmail) && (
<span className="block text-[11px]">
{[accountPhone, accountEmail].filter(Boolean).join(' · ')}
</span>
)}
</span>
) : (
<span>Walk-in tap to add customer</span>
)}
</button>
</div>
{/* Line items */}
@@ -182,6 +202,9 @@ export function POSCartPanel({ transaction }: POSCartPanelProps) {
onComplete={handlePaymentComplete}
/>
)}
{/* Customer dialog */}
<POSCustomerDialog open={customerOpen} onOpenChange={setCustomerOpen} />
</div>
)
}