rfunds-online/components/pay/RequestFlow.tsx

247 lines
11 KiB
TypeScript

'use client'
import { useState, useCallback } from 'react'
import { useAuthStore } from '@/lib/auth'
type Step = 'details' | 'share'
interface RequestFlowProps {
onCreated?: (link: string) => void
}
const CURRENCIES = [
{ code: 'USD', symbol: '$' },
{ code: 'EUR', symbol: '\u20ac' },
{ code: 'GBP', symbol: '\u00a3' },
]
export default function RequestFlow({ onCreated }: RequestFlowProps) {
const { isAuthenticated, username, login } = useAuthStore()
const [step, setStep] = useState<Step>('details')
const [amount, setAmount] = useState('')
const [currency, setCurrency] = useState('USD')
const [description, setDescription] = useState('')
const [recipientEmail, setRecipientEmail] = useState('')
const [paymentLink, setPaymentLink] = useState('')
const [copied, setCopied] = useState(false)
const numericAmount = parseFloat(amount) || 0
const selectedCurrency = CURRENCIES.find(c => c.code === currency)
const handleCreate = useCallback(() => {
// Build the payment link with prefilled params
const params = new URLSearchParams()
if (numericAmount > 0) params.set('amount', numericAmount.toString())
if (currency !== 'USD') params.set('currency', currency)
if (recipientEmail) params.set('to', recipientEmail)
if (username) params.set('name', username)
if (description) params.set('note', description)
const baseUrl = typeof window !== 'undefined' ? window.location.origin : 'https://rfunds.online'
const link = `${baseUrl}/pay?${params.toString()}`
setPaymentLink(link)
setStep('share')
onCreated?.(link)
}, [numericAmount, currency, recipientEmail, username, description, onCreated])
const handleCopy = useCallback(() => {
navigator.clipboard.writeText(paymentLink)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
}, [paymentLink])
return (
<div className="min-h-screen bg-slate-50 flex items-start justify-center pt-8 pb-16 px-4">
<div className="w-full max-w-[480px]">
{/* Header */}
<div className="flex items-center justify-between mb-6">
<h1 className="text-lg font-semibold text-slate-900">
{step === 'share' ? 'Share payment link' : 'Request money'}
</h1>
{isAuthenticated ? (
<div className="flex items-center gap-2 px-3 py-1.5 bg-white rounded-full border border-slate-200 text-sm text-slate-600">
<span className="w-2 h-2 rounded-full bg-emerald-500" />
{username}
</div>
) : (
<button onClick={login} className="px-3 py-1.5 bg-emerald-600 text-white rounded-full text-sm font-medium hover:bg-emerald-500 transition-colors">
Sign in
</button>
)}
</div>
{/* ── Step 1: Details ── */}
{step === 'details' && (
<div className="bg-white rounded-2xl border border-slate-200 shadow-sm overflow-hidden">
<div className="p-6">
<label className="text-sm font-medium text-slate-500 block mb-4">How much do you want to receive?</label>
<div className="flex items-center gap-3 mb-6">
<input
type="text"
inputMode="decimal"
value={amount}
onChange={e => {
const v = e.target.value.replace(/[^0-9.]/g, '')
if (v.split('.').length <= 2) setAmount(v)
}}
placeholder="0.00"
className="flex-1 text-4xl font-light text-slate-900 outline-none bg-transparent placeholder:text-slate-300"
autoFocus
/>
<select
value={currency}
onChange={e => setCurrency(e.target.value)}
className="text-lg font-medium text-slate-700 bg-slate-100 px-3 py-2 rounded-xl border-0 outline-none cursor-pointer hover:bg-slate-200 transition-colors appearance-none"
>
{CURRENCIES.map(c => (
<option key={c.code} value={c.code}>{c.code}</option>
))}
</select>
</div>
<div className="space-y-4">
<div>
<label className="text-xs font-medium text-slate-600 block mb-1.5">Your email (to receive notification)</label>
<input
type="email"
value={recipientEmail}
onChange={e => setRecipientEmail(e.target.value)}
placeholder="you@example.com"
className="w-full px-4 py-3 border border-slate-200 rounded-xl text-sm text-slate-900 outline-none focus:border-emerald-400 focus:ring-2 focus:ring-emerald-100 transition-all"
/>
</div>
<div>
<label className="text-xs font-medium text-slate-600 block mb-1.5">What's it for? (shown to payer)</label>
<input
type="text"
value={description}
onChange={e => setDescription(e.target.value)}
placeholder="e.g. Dinner split, Invoice #123"
className="w-full px-4 py-3 border border-slate-200 rounded-xl text-sm text-slate-900 outline-none focus:border-emerald-400 focus:ring-2 focus:ring-emerald-100 transition-all"
/>
</div>
</div>
</div>
<div className="border-t border-slate-100 p-4">
<button
onClick={handleCreate}
disabled={numericAmount < 1}
className="w-full py-3.5 bg-emerald-600 text-white rounded-xl text-sm font-semibold hover:bg-emerald-500 disabled:bg-slate-200 disabled:text-slate-400 transition-colors"
>
Create payment link
</button>
</div>
</div>
)}
{/* ── Step 2: Share ── */}
{step === 'share' && (
<div className="bg-white rounded-2xl border border-slate-200 shadow-sm overflow-hidden">
<div className="p-6 text-center">
<div className="w-16 h-16 rounded-full bg-violet-100 flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8 text-violet-600" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
<path strokeLinecap="round" strokeLinejoin="round" d="M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m9.86-4.318a4.5 4.5 0 00-7.093-1.657l-4.5 4.5a4.5 4.5 0 006.364 6.364l1.757-1.757" />
</svg>
</div>
<h2 className="text-lg font-semibold text-slate-900 mb-1">Payment link ready</h2>
<p className="text-sm text-slate-500 mb-6">
Share this link to receive {selectedCurrency?.symbol}{numericAmount.toFixed(2)} {currency}
</p>
{/* Link display */}
<div className="bg-slate-50 rounded-xl p-4 mb-4">
<div className="flex items-center gap-2">
<input
type="text"
readOnly
value={paymentLink}
className="flex-1 text-xs text-slate-600 bg-transparent outline-none font-mono truncate"
/>
<button
onClick={handleCopy}
className={`px-3 py-1.5 rounded-lg text-xs font-medium transition-colors flex-shrink-0 ${
copied
? 'bg-emerald-100 text-emerald-700'
: 'bg-emerald-600 text-white hover:bg-emerald-500'
}`}
>
{copied ? 'Copied!' : 'Copy'}
</button>
</div>
</div>
{/* Summary */}
<div className="bg-slate-50 rounded-xl p-4 text-left space-y-2 mb-4">
<div className="flex justify-between text-sm">
<span className="text-slate-500">Amount</span>
<span className="font-medium text-slate-900">{selectedCurrency?.symbol}{numericAmount.toFixed(2)} {currency}</span>
</div>
{description && (
<div className="flex justify-between text-sm">
<span className="text-slate-500">Note</span>
<span className="text-slate-700">{description}</span>
</div>
)}
{recipientEmail && (
<div className="flex justify-between text-sm">
<span className="text-slate-500">Notify</span>
<span className="text-slate-700">{recipientEmail}</span>
</div>
)}
</div>
{/* Share options */}
<div className="flex gap-2">
<a
href={`mailto:?subject=${encodeURIComponent(`Payment request: ${selectedCurrency?.symbol}${numericAmount.toFixed(2)}`)}&body=${encodeURIComponent(`Hi,\n\nI'm requesting a payment of ${selectedCurrency?.symbol}${numericAmount.toFixed(2)} ${currency}${description ? ` for: ${description}` : ''}.\n\nYou can pay here: ${paymentLink}\n\nThanks!`)}`}
className="flex-1 py-2.5 bg-slate-100 text-slate-700 rounded-xl text-xs font-medium hover:bg-slate-200 transition-colors text-center"
>
Email link
</a>
<button
onClick={() => {
if (navigator.share) {
navigator.share({
title: `Payment request: ${selectedCurrency?.symbol}${numericAmount.toFixed(2)}`,
text: description || `Payment of ${selectedCurrency?.symbol}${numericAmount.toFixed(2)} ${currency}`,
url: paymentLink,
}).catch(() => {})
} else {
handleCopy()
}
}}
className="flex-1 py-2.5 bg-slate-100 text-slate-700 rounded-xl text-xs font-medium hover:bg-slate-200 transition-colors"
>
Share
</button>
</div>
</div>
<div className="border-t border-slate-100 p-4">
<button
onClick={() => { setStep('details'); setAmount(''); setDescription(''); setPaymentLink('') }}
className="w-full py-3 bg-white border border-slate-200 text-slate-700 rounded-xl text-sm font-medium hover:bg-slate-50 transition-colors"
>
Create another request
</button>
</div>
</div>
)}
{/* Footer */}
<div className="mt-6 text-center">
<p className="text-xs text-slate-400">
Powered by <span className="font-medium">rSpace</span> Payment Infrastructure
</p>
</div>
</div>
</div>
)
}