feat: receipt customization settings tab with header, footer, policy, social
- New Receipt tab in Settings page with editable fields - receipt_header: text below logo (e.g. tagline) - receipt_footer: thank you message - receipt_return_policy: return policy text - receipt_social: website/social media - All stored in app_config, rendered on printed receipts - Seeded in migration with empty defaults Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -63,6 +63,20 @@ export function POSPaymentDialog({ open, onOpenChange, paymentMethod, transactio
|
||||
|
||||
const QUICK_AMOUNTS = [1, 5, 10, 20, 50, 100]
|
||||
|
||||
// Fetch receipt config
|
||||
interface AppConfigEntry { key: string; value: string | null }
|
||||
const { data: configData } = useQuery({
|
||||
queryKey: ['config'],
|
||||
queryFn: () => api.get<{ data: AppConfigEntry[] }>('/v1/config'),
|
||||
enabled: !!result?.id,
|
||||
})
|
||||
const receiptConfig = {
|
||||
header: configData?.data?.find((c) => c.key === 'receipt_header')?.value || undefined,
|
||||
footer: configData?.data?.find((c) => c.key === 'receipt_footer')?.value || undefined,
|
||||
returnPolicy: configData?.data?.find((c) => c.key === 'receipt_return_policy')?.value || undefined,
|
||||
social: configData?.data?.find((c) => c.key === 'receipt_social')?.value || undefined,
|
||||
}
|
||||
|
||||
// Fetch full receipt data after completion
|
||||
const { data: receiptData } = useQuery({
|
||||
queryKey: ['pos', 'receipt', result?.id],
|
||||
@@ -91,7 +105,7 @@ export function POSPaymentDialog({ open, onOpenChange, paymentMethod, transactio
|
||||
</Button>
|
||||
</div>
|
||||
<div className="print:block">
|
||||
<POSReceipt data={receiptData} footerText="Thank you for your business!" />
|
||||
<POSReceipt data={receiptData} config={receiptConfig} />
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@@ -45,10 +45,18 @@ interface ReceiptData {
|
||||
}
|
||||
}
|
||||
|
||||
interface ReceiptConfig {
|
||||
header?: string
|
||||
footer?: string
|
||||
returnPolicy?: string
|
||||
social?: string
|
||||
}
|
||||
|
||||
interface POSReceiptProps {
|
||||
data: ReceiptData
|
||||
size?: 'thermal' | 'full'
|
||||
footerText?: string
|
||||
config?: ReceiptConfig
|
||||
}
|
||||
|
||||
function useStoreLogo(companyId?: string) {
|
||||
@@ -91,7 +99,7 @@ function useStoreLogo(companyId?: string) {
|
||||
return logoSrc
|
||||
}
|
||||
|
||||
export function POSReceipt({ data, size = 'thermal', footerText }: POSReceiptProps) {
|
||||
export function POSReceipt({ data, size = 'thermal', footerText, config }: POSReceiptProps) {
|
||||
const barcodeRef = useRef<SVGSVGElement>(null)
|
||||
const { transaction: txn, company, location } = data
|
||||
const isThermal = size === 'thermal'
|
||||
@@ -146,6 +154,7 @@ export function POSReceipt({ data, size = 'thermal', footerText }: POSReceiptPro
|
||||
</>
|
||||
)}
|
||||
{phone && <div>{phone}</div>}
|
||||
{config?.header && <div className="mt-1 text-gray-600">{config.header}</div>}
|
||||
</div>
|
||||
|
||||
{/* Transaction info */}
|
||||
@@ -234,8 +243,14 @@ export function POSReceipt({ data, size = 'thermal', footerText }: POSReceiptPro
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
{footerText && (
|
||||
<div className="text-center text-gray-500 pb-2">{footerText}</div>
|
||||
{(config?.footer || footerText) && (
|
||||
<div className="text-center text-gray-500 pb-1">{config?.footer || footerText}</div>
|
||||
)}
|
||||
{config?.returnPolicy && (
|
||||
<div className="text-center text-gray-400 text-[10px] pb-1">{config.returnPolicy}</div>
|
||||
)}
|
||||
{config?.social && (
|
||||
<div className="text-center text-gray-500 pb-2">{config.social}</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user