feat: show company logo on receipt if uploaded, fall back to name

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
ryan
2026-04-04 21:19:50 +00:00
parent 3519db9bd9
commit 0aa9345c27

View File

@@ -1,4 +1,8 @@
import { useEffect, useRef } from 'react'
import { useEffect, useRef, useState } from 'react'
import { useQuery } from '@tanstack/react-query'
import { queryOptions } from '@tanstack/react-query'
import { api } from '@/lib/api-client'
import { usePOSStore } from '@/stores/pos.store'
import JsBarcode from 'jsbarcode'
interface ReceiptLineItem {
@@ -47,10 +51,51 @@ interface POSReceiptProps {
footerText?: string
}
function useStoreLogo(companyId?: string) {
const token = usePOSStore((s) => s.token)
const [logoSrc, setLogoSrc] = useState<string | null>(null)
const { data: storeData } = useQuery(queryOptions({
queryKey: ['store'],
queryFn: () => api.get<{ id: string }>('/v1/store'),
enabled: !!token,
}))
const storeId = companyId ?? storeData?.id
const { data: filesData } = useQuery(queryOptions({
queryKey: ['files', 'company', storeId ?? ''],
queryFn: () => api.get<{ data: { id: string; path: string }[] }>('/v1/files', { entityType: 'company', entityId: storeId }),
enabled: !!storeId,
}))
const logoFile = filesData?.data?.find((f) => f.path.includes('/logo-'))
useEffect(() => {
if (!logoFile || !token) { setLogoSrc(null); return }
let cancelled = false
let blobUrl: string | null = null
async function load() {
try {
const res = await fetch(`/v1/files/serve/${logoFile!.path}`, {
headers: { Authorization: `Bearer ${token}` },
})
if (!res.ok || cancelled) return
const blob = await res.blob()
if (!cancelled) { blobUrl = URL.createObjectURL(blob); setLogoSrc(blobUrl) }
} catch { /* ignore */ }
}
load()
return () => { cancelled = true; if (blobUrl) URL.revokeObjectURL(blobUrl) }
}, [logoFile?.path, token])
return logoSrc
}
export function POSReceipt({ data, size = 'thermal', footerText }: POSReceiptProps) {
const barcodeRef = useRef<SVGSVGElement>(null)
const { transaction: txn, company, location } = data
const isThermal = size === 'thermal'
const logoSrc = useStoreLogo()
useEffect(() => {
if (barcodeRef.current) {
@@ -84,7 +129,11 @@ export function POSReceipt({ data, size = 'thermal', footerText }: POSReceiptPro
>
{/* Store header */}
<div className="text-center pb-2 border-b border-dashed border-gray-400">
{logoSrc ? (
<img src={logoSrc} alt={company.name} className={`mx-auto mb-1 ${isThermal ? 'max-h-12 max-w-[200px]' : 'max-h-16 max-w-[280px]'} object-contain`} />
) : (
<div className={`font-bold ${isThermal ? 'text-sm' : 'text-lg'}`}>{company.name}</div>
)}
{location.name !== company.name && (
<div className="text-gray-600">{location.name}</div>
)}