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