| 1 | import { useState, useEffect } from "react"; |
| 2 | import { useNavigate, useLocation } from "react-router-dom"; |
| 3 | import Layout from "@/components/Layout"; |
| 4 | import { Card } from "@/components/ui/card"; |
| 5 | import { Input } from "@/components/ui/input"; |
| 6 | import { Button } from "@/components/ui/button"; |
| 7 | import { Label } from "@/components/ui/label"; |
| 8 | import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; |
| 9 | import { ArrowRight, CheckCircle, Loader2 } from "lucide-react"; |
| 10 | import { transactionService } from "@/services/transactionService"; |
| 11 | import { Currency } from "@/types/transaction"; |
| 12 | import { toast } from "@/hooks/use-toast"; |
| 13 | |
| 14 | const SendMoney = () => { |
| 15 | const navigate = useNavigate(); |
| 16 | const location = useLocation(); |
| 17 | const [step, setStep] = useState<"form" | "processing" | "success">("form"); |
| 18 | const [amount, setAmount] = useState(""); |
| 19 | const [currency, setCurrency] = useState<Currency>("USD"); |
| 20 | const [recipient, setRecipient] = useState(""); |
| 21 | const [recipientName, setRecipientName] = useState(""); |
| 22 | const [description, setDescription] = useState(""); |
| 23 | const [transactionId, setTransactionId] = useState(""); |
| 24 | |
| 25 | // Pre-fill recipient data if passed from Recipients page |
| 26 | useEffect(() => { |
| 27 | if (location.state?.recipient) { |
| 28 | const { name, email } = location.state.recipient; |
| 29 | setRecipientName(name); |
| 30 | setRecipient(email); |
| 31 | } |
| 32 | }, [location]); |
| 33 | |
| 34 | const fee = amount ? transactionService.calculateFee(parseFloat(amount)) : 0; |
| 35 | const total = amount ? parseFloat(amount) + fee : 0; |
| 36 | |
| 37 | const handleSend = async () => { |
| 38 | if (!amount || !recipient || !recipientName) { |
| 39 | toast({ |
| 40 | title: "Missing Information", |
| 41 | description: "Please fill in all required fields", |
| 42 | variant: "destructive", |
| 43 | }); |
| 44 | return; |
| 45 | } |
| 46 | |
| 47 | setStep("processing"); |
| 48 | |
| 49 | try { |
| 50 | const transaction = await transactionService.sendMoney( |
| 51 | parseFloat(amount), |
| 52 | currency, |
| 53 | recipient, |
| 54 | recipientName, |
| 55 | description |
| 56 | ); |
| 57 | |
| 58 | setTransactionId(transaction.id); |
| 59 | setStep("success"); |
| 60 | } catch (error) { |
| 61 | toast({ |
| 62 | title: "Transaction Failed", |
| 63 | description: "Unable to process payment. Please try again.", |
| 64 | variant: "destructive", |
| 65 | }); |
| 66 | setStep("form"); |
| 67 | } |
| 68 | }; |
| 69 | |
| 70 | if (step === "processing") { |
| 71 | return ( |
| 72 | <Layout title="Sending"> |
| 73 | <div className="min-h-[60vh] flex items-center justify-center p-4"> |
| 74 | <Card className="p-12 border-0 shadow-lg text-center max-w-md w-full"> |
| 75 | <Loader2 className="h-16 w-16 text-primary animate-spin mx-auto mb-6" /> |
| 76 | <h3 className="text-2xl font-bold mb-3">Processing Payment</h3> |
| 77 | <p className="text-muted-foreground font-medium"> |
| 78 | Please wait while we securely process your transaction... |
| 79 | </p> |
| 80 | </Card> |
| 81 | </div> |
| 82 | </Layout> |
| 83 | ); |
| 84 | } |
| 85 | |
| 86 | if (step === "success") { |
| 87 | return ( |
| 88 | <Layout title="Success"> |
| 89 | <div className="min-h-[60vh] flex items-center justify-center p-4"> |
| 90 | <Card className="p-12 border-0 shadow-lg text-center max-w-md w-full space-y-6"> |
| 91 | <CheckCircle className="h-20 w-20 text-expense-positive mx-auto" /> |
| 92 | <div> |
| 93 | <h3 className="text-2xl font-bold mb-3">Payment Sent!</h3> |
| 94 | <p className="text-muted-foreground font-medium mb-2"> |
| 95 | Your payment has been successfully processed |
| 96 | </p> |
| 97 | <p className="text-sm text-muted-foreground"> |
| 98 | Transaction ID: {transactionId} |
| 99 | </p> |
| 100 | </div> |
| 101 | <div className="space-y-3 pt-4"> |
| 102 | <Button className="w-full" onClick={() => navigate("/")}> |
| 103 | View Transactions |
| 104 | </Button> |
| 105 | <Button |
| 106 | variant="outline" |
| 107 | className="w-full" |
| 108 | onClick={() => { |
| 109 | setStep("form"); |
| 110 | setAmount(""); |
| 111 | setRecipient(""); |
| 112 | setRecipientName(""); |
| 113 | setDescription(""); |
| 114 | }} |
| 115 | > |
| 116 | Send Another Payment |
| 117 | </Button> |
| 118 | </div> |
| 119 | </Card> |
| 120 | </div> |
| 121 | </Layout> |
| 122 | ); |
| 123 | } |
| 124 | |
| 125 | return ( |
| 126 | <Layout title="Send Money"> |
| 127 | <div className="p-4 space-y-6"> |
| 128 | <Card className="p-6 border-0 shadow-lg"> |
| 129 | <div className="space-y-6"> |
| 130 | <div className="space-y-2"> |
| 131 | <Label htmlFor="amount" className="text-base font-semibold"> |
| 132 | Amount |
| 133 | </Label> |
| 134 | <div className="flex gap-2"> |
| 135 | <Input |
| 136 | id="amount" |
| 137 | type="number" |
| 138 | placeholder="0.00" |
| 139 | value={amount} |
| 140 | onChange={(e) => setAmount(e.target.value)} |
| 141 | className="text-2xl h-14 font-bold" |
| 142 | /> |
| 143 | <Select value={currency} onValueChange={(val) => setCurrency(val as Currency)}> |
| 144 | <SelectTrigger className="w-28 h-14"> |
| 145 | <SelectValue /> |
| 146 | </SelectTrigger> |
| 147 | <SelectContent> |
| 148 | <SelectItem value="USD">USD</SelectItem> |
| 149 | <SelectItem value="EUR">EUR</SelectItem> |
| 150 | <SelectItem value="GBP">GBP</SelectItem> |
| 151 | <SelectItem value="NGN">NGN</SelectItem> |
| 152 | <SelectItem value="KES">KES</SelectItem> |
| 153 | <SelectItem value="GHS">GHS</SelectItem> |
| 154 | </SelectContent> |
| 155 | </Select> |
| 156 | </div> |
| 157 | </div> |
| 158 | |
| 159 | <div className="space-y-2"> |
| 160 | <Label htmlFor="recipientName" className="text-base font-semibold"> |
| 161 | Recipient Name |
| 162 | </Label> |
| 163 | <Input |
| 164 | id="recipientName" |
| 165 | placeholder="John Doe" |
| 166 | value={recipientName} |
| 167 | onChange={(e) => setRecipientName(e.target.value)} |
| 168 | className="h-12" |
| 169 | /> |
| 170 | </div> |
| 171 | |
| 172 | <div className="space-y-2"> |
| 173 | <Label htmlFor="recipient" className="text-base font-semibold"> |
| 174 | Email Address |
| 175 | </Label> |
| 176 | <Input |
| 177 | id="recipient" |
| 178 | type="email" |
| 179 | placeholder="recipient@email.com" |
| 180 | value={recipient} |
| 181 | onChange={(e) => setRecipient(e.target.value)} |
| 182 | className="h-12" |
| 183 | /> |
| 184 | </div> |
| 185 | |
| 186 | <div className="space-y-2"> |
| 187 | <Label htmlFor="description" className="text-base font-semibold"> |
| 188 | Description (Optional) |
| 189 | </Label> |
| 190 | <Input |
| 191 | id="description" |
| 192 | placeholder="What's this payment for?" |
| 193 | value={description} |
| 194 | onChange={(e) => setDescription(e.target.value)} |
| 195 | className="h-12" |
| 196 | /> |
| 197 | </div> |
| 198 | </div> |
| 199 | </Card> |
| 200 | |
| 201 | {amount && ( |
| 202 | <Card className="p-6 border-0 shadow-md space-y-4"> |
| 203 | <h3 className="font-bold text-lg">Payment Summary</h3> |
| 204 | <div className="space-y-3"> |
| 205 | <div className="flex justify-between text-base"> |
| 206 | <span className="text-muted-foreground">Amount</span> |
| 207 | <span className="font-semibold"> |
| 208 | {currency} {parseFloat(amount).toFixed(2)} |
| 209 | </span> |
| 210 | </div> |
| 211 | <div className="flex justify-between text-base"> |
| 212 | <span className="text-muted-foreground">Fee</span> |
| 213 | <span className="font-semibold"> |
| 214 | {currency} {fee.toFixed(2)} |
| 215 | </span> |
| 216 | </div> |
| 217 | <div className="border-t pt-3 flex justify-between text-lg"> |
| 218 | <span className="font-bold">Total</span> |
| 219 | <span className="font-bold"> |
| 220 | {currency} {total.toFixed(2)} |
| 221 | </span> |
| 222 | </div> |
| 223 | </div> |
| 224 | </Card> |
| 225 | )} |
| 226 | |
| 227 | <Button |
| 228 | size="lg" |
| 229 | className="w-full" |
| 230 | onClick={handleSend} |
| 231 | disabled={!amount || !recipient || !recipientName} |
| 232 | > |
| 233 | Send Payment |
| 234 | <ArrowRight className="ml-2 h-5 w-5" /> |
| 235 | </Button> |
| 236 | </div> |
| 237 | </Layout> |
| 238 | ); |
| 239 | }; |
| 240 | |
| 241 | export default SendMoney; |