epic-6-snap-hybrid-complete #18

Merged
root merged 2 commits from epic-6-snap-hybrid-complete into dev 2025-12-04 10:11:23 +00:00
4 changed files with 2 additions and 22 deletions

View File

@ -6,32 +6,16 @@ function formatCurrencyIDR(amount: number) {
return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', maximumFractionDigits: 0 }).format(amount) 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 { export interface PaymentSheetProps {
merchantName?: string merchantName?: string
orderId: string orderId: string
amount: number amount: number
expireAt: number // epoch ms
customerName?: string customerName?: string
children?: React.ReactNode children?: React.ReactNode
showStatusCTA?: boolean showStatusCTA?: boolean
} }
export function PaymentSheet({ merchantName = 'Simaya', orderId, amount, expireAt, customerName, children, showStatusCTA = true }: PaymentSheetProps) { export function PaymentSheet({ merchantName = 'Simaya', orderId, amount, customerName, children, showStatusCTA = true }: PaymentSheetProps) {
const countdown = useCountdown(expireAt)
const [expanded, setExpanded] = React.useState(true) const [expanded, setExpanded] = React.useState(true)
return ( return (
<div className="max-w-md"> <div className="max-w-md">

View File

@ -215,7 +215,6 @@ export function CheckoutPage() {
} }
const orderId = orderIdRef.current const orderId = orderIdRef.current
const amount = 3500000 const amount = 3500000
const expireAt = Date.now() + 59 * 60 * 1000 + 32 * 1000 // 00:59:32
const [selected, setSelected] = React.useState<PaymentMethod | null>(null) const [selected, setSelected] = React.useState<PaymentMethod | null>(null)
const [currentStep, setCurrentStep] = React.useState<1 | 2>(1) const [currentStep, setCurrentStep] = React.useState<1 | 2>(1)
const [isBusy, setIsBusy] = React.useState(false) const [isBusy, setIsBusy] = React.useState(false)
@ -259,7 +258,7 @@ export function CheckoutPage() {
</Alert> </Alert>
)} )}
<PaymentSheet merchantName="Simaya" orderId={orderId} amount={amount} expireAt={expireAt} customerName={form.name} showStatusCTA={modalClosed}> <PaymentSheet merchantName="Simaya" orderId={orderId} amount={amount} customerName={form.name} showStatusCTA={modalClosed}>
{/* Wizard 2 langkah: Step 1 (Form Dummy) → Step 2 (Payment - Snap/Core auto-detect) */} {/* Wizard 2 langkah: Step 1 (Form Dummy) → Step 2 (Payment - Snap/Core auto-detect) */}
{currentStep === 1 && ( {currentStep === 1 && (
<div className="space-y-3"> <div className="space-y-3">

View File

@ -241,7 +241,6 @@ export function PayPage() {
merchantName={merchantName} merchantName={merchantName}
orderId={orderId || (token ?? '')} orderId={orderId || (token ?? '')}
amount={amount} amount={amount}
expireAt={expireAt}
showStatusCTA={false} showStatusCTA={false}
> >
<div className="space-y-4 px-4 py-6"> <div className="space-y-4 px-4 py-6">
@ -271,7 +270,6 @@ export function PayPage() {
merchantName={merchantName} merchantName={merchantName}
orderId={orderId} orderId={orderId}
amount={amount} amount={amount}
expireAt={expireAt}
showStatusCTA={currentStep === 2} showStatusCTA={currentStep === 2}
> >
<div className="space-y-4 px-4 py-6"> <div className="space-y-4 px-4 py-6">

View File

@ -21,7 +21,6 @@ export function PaymentStatusPage() {
const statusText = data?.status ?? 'pending' const statusText = data?.status ?? 'pending'
const isFinal = ['settlement', 'capture', 'expire', 'cancel', 'deny', 'refund', 'chargeback'].includes(statusText) const isFinal = ['settlement', 'capture', 'expire', 'cancel', 'deny', 'refund', 'chargeback'].includes(statusText)
const isSuccess = statusText === 'settlement' || statusText === 'capture'
function sanitizeUrl(u?: string) { function sanitizeUrl(u?: string) {
return (u || '').replace(/[`\s]+$/g, '').replace(/^\s+|\s+$/g, '').replace(/`/g, '') return (u || '').replace(/[`\s]+$/g, '').replace(/^\s+|\s+$/g, '').replace(/`/g, '')
} }