89 lines
3.7 KiB
TypeScript
89 lines
3.7 KiB
TypeScript
import React from 'react'
|
|
import { Link } from 'react-router-dom'
|
|
import { TrustStrip } from './TrustStrip'
|
|
|
|
function formatCurrencyIDR(amount: number) {
|
|
return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', maximumFractionDigits: 0 }).format(amount)
|
|
}
|
|
|
|
function useCountdown(expireAt: number) {
|
|
const [now, setNow] = React.useState(() => Date.now())
|
|
React.useEffect(() => {
|
|
const id = setInterval(() => setNow(Date.now()), 1000)
|
|
return () => clearInterval(id)
|
|
}, [])
|
|
const remainMs = Math.max(0, expireAt - now)
|
|
const totalSec = Math.floor(remainMs / 1000)
|
|
const hh = String(Math.floor(totalSec / 3600)).padStart(2, '0')
|
|
const mm = String(Math.floor((totalSec % 3600) / 60)).padStart(2, '0')
|
|
const ss = String(totalSec % 60).padStart(2, '0')
|
|
return `${hh}:${mm}:${ss}`
|
|
}
|
|
|
|
export interface PaymentSheetProps {
|
|
merchantName?: string
|
|
orderId: string
|
|
amount: number
|
|
expireAt: number // epoch ms
|
|
customerName?: string
|
|
children?: React.ReactNode
|
|
showStatusCTA?: boolean
|
|
}
|
|
|
|
export function PaymentSheet({ merchantName = 'Simaya', orderId, amount, expireAt, customerName, children, showStatusCTA = true }: PaymentSheetProps) {
|
|
const countdown = useCountdown(expireAt)
|
|
const [expanded, setExpanded] = React.useState(true)
|
|
return (
|
|
<div className="max-w-md">
|
|
<div className="card overflow-hidden">
|
|
{/* Header */}
|
|
<div className="bg-[#0c1f3f] text-white p-3 sm:p-4 flex items-center justify-between">
|
|
<div className="flex items-center gap-2 sm:gap-3">
|
|
<div className="rounded bg-white text-black px-2 py-1 text-[11px] sm:text-xs font-bold" aria-hidden>
|
|
SIMAYA
|
|
</div>
|
|
<div className="font-semibold text-sm sm:text-base">{merchantName}</div>
|
|
</div>
|
|
<button
|
|
aria-label={expanded ? 'Collapse' : 'Expand'}
|
|
aria-expanded={expanded}
|
|
onClick={() => setExpanded((v) => !v)}
|
|
className={`text-white/80 transition-transform ${expanded ? '' : 'rotate-180'} focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-white/80 focus-visible:ring-offset-2`}
|
|
>
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" aria-hidden>
|
|
<path d="M7 14L12 9L17 14" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
{expanded && (
|
|
<div className="p-4 border-b border-black/10 flex items-start justify-between">
|
|
<div>
|
|
<div className="text-xs text-black">Total</div>
|
|
<div className="text-xl font-semibold">{formatCurrencyIDR(amount)}</div>
|
|
<div className="text-xs text-black/60">Order ID #{orderId}</div>
|
|
{customerName && <div className="text-xs text-black/60 mt-1">Nama: {customerName}</div>}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<div className="p-4">
|
|
{children}
|
|
<TrustStrip location="sheet" />
|
|
</div>
|
|
{showStatusCTA && (
|
|
<div className="sticky bottom-0 bg-white/95 backdrop-blur border-t border-black/10 p-3 pb-[env(safe-area-inset-bottom)]">
|
|
<Link
|
|
to={`/payments/${orderId}/status`}
|
|
aria-label="Buka halaman Status Pembayaran"
|
|
className="w-full block text-center rounded bg-[#0c1f3f] !text-white py-3 text-base font-semibold hover:bg-[#0a1a35] hover:!text-white focus:outline-none focus-visible:ring-3 focus-visible:ring-offset-2 focus-visible:ring-[#0c1f3f] visited:!text-white active:!text-white"
|
|
>
|
|
Cek status pembayaran
|
|
</Link>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|