261 lines
8.2 KiB
TypeScript
261 lines
8.2 KiB
TypeScript
import React, { useEffect, useRef, useState } from "react";
|
|
import {
|
|
View,
|
|
Text,
|
|
ScrollView,
|
|
TouchableOpacity,
|
|
FlatList,
|
|
} from "react-native";
|
|
import { Input } from "~/components/ui/input";
|
|
import { Button } from "~/components/ui/button";
|
|
import { Filter, LucideX, LucideCreditCard } from "lucide-react-native";
|
|
import AccountCard from "~/components/ui/accCard";
|
|
import BackButton from "~/components/ui/backButton";
|
|
import { useLocalSearchParams, router } from "expo-router";
|
|
import { useAuthWithProfile } from "~/lib/hooks/useAuthWithProfile";
|
|
import { useUserWallet } from "~/lib/hooks/useUserWallet";
|
|
import { CreditCard, WalletService } from "~/lib/services/walletService";
|
|
import { ROUTES } from "~/lib/routes";
|
|
import { showAlert } from "~/lib/utils/alertUtils";
|
|
import ScreenWrapper from "~/components/ui/ScreenWrapper";
|
|
import ModalToast from "~/components/ui/toast";
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
export default function SelectAccount() {
|
|
const { t } = useTranslation();
|
|
const { amount } = useLocalSearchParams<{ amount: string }>();
|
|
const { user } = useAuthWithProfile();
|
|
const { wallet, loading, error, refreshWallet } = useUserWallet(user);
|
|
const [selectedCardId, setSelectedCardId] = React.useState<string | null>(
|
|
null
|
|
);
|
|
const [isProcessing, setIsProcessing] = useState(false);
|
|
|
|
const [toastVisible, setToastVisible] = useState(false);
|
|
const [toastTitle, setToastTitle] = useState("");
|
|
const [toastDescription, setToastDescription] = useState<string | undefined>(
|
|
undefined
|
|
);
|
|
const [toastVariant, setToastVariant] = useState<
|
|
"success" | "error" | "warning" | "info"
|
|
>("info");
|
|
const toastTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
|
|
const showToast = (
|
|
title: string,
|
|
description?: string,
|
|
variant: "success" | "error" | "warning" | "info" = "info"
|
|
) => {
|
|
if (toastTimeoutRef.current) {
|
|
clearTimeout(toastTimeoutRef.current);
|
|
}
|
|
|
|
setToastTitle(title);
|
|
setToastDescription(description);
|
|
setToastVariant(variant);
|
|
setToastVisible(true);
|
|
|
|
toastTimeoutRef.current = setTimeout(() => {
|
|
setToastVisible(false);
|
|
toastTimeoutRef.current = null;
|
|
}, 2500);
|
|
};
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
if (toastTimeoutRef.current) {
|
|
clearTimeout(toastTimeoutRef.current);
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
const handleCardSelect = (card: CreditCard) => {
|
|
setSelectedCardId(card.id);
|
|
console.log("Selected card:", card.id);
|
|
};
|
|
|
|
const renderCards = () => {
|
|
if (loading) {
|
|
return (
|
|
<View className="flex items-center justify-center py-10">
|
|
<Text className="text-gray-500 font-dmsans">
|
|
{t("selectacc.loadingCards")}
|
|
</Text>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<View className="flex items-center justify-center py-10">
|
|
<Text className="text-red-500 font-dmsans">
|
|
{t("selectacc.errorTitle")}
|
|
</Text>
|
|
<Text className="text-gray-400 font-dmsans-medium text-sm mt-1">
|
|
{t("selectacc.errorWithMessage", { error })}
|
|
</Text>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
if (!wallet?.cards || wallet.cards.length === 0) {
|
|
return (
|
|
<View className="flex items-center justify-center py-10">
|
|
<LucideCreditCard color="#D1D5DB" size={48} />
|
|
<Text className="text-gray-500 font-dmsans mt-4">
|
|
{t("selectacc.emptyTitle")}
|
|
</Text>
|
|
<Text className="text-gray-400 font-dmsans-medium text-sm mt-1 text-center px-4">
|
|
{t("selectacc.emptySubtitle")}
|
|
</Text>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<FlatList
|
|
data={wallet.cards}
|
|
keyExtractor={(item) => item.id}
|
|
scrollEnabled={false}
|
|
ItemSeparatorComponent={() => <View className="h-2" />}
|
|
renderItem={({ item: card }) => (
|
|
<TouchableOpacity
|
|
onPress={() => handleCardSelect(card)}
|
|
className="w-full"
|
|
>
|
|
<AccountCard
|
|
cardType={card.cardType}
|
|
cardNumber={card.cardNumber}
|
|
expiryDate={card.expiryDate}
|
|
selected={selectedCardId === card.id}
|
|
/>
|
|
</TouchableOpacity>
|
|
)}
|
|
/>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<ScreenWrapper edges={[]}>
|
|
<BackButton />
|
|
|
|
<View className=" px-5 pt-10">
|
|
<Text className="text-xl font-dmsans-medium text-center">
|
|
{t("selectacc.title")}
|
|
</Text>
|
|
{amount && (
|
|
<Text className="text-lg font-dmsans text-primary mt-8">
|
|
{t("selectacc.addingAmount", {
|
|
amount: (parseInt(amount) / 100).toFixed(2),
|
|
})}
|
|
</Text>
|
|
)}
|
|
</View>
|
|
<ScrollView className="flex px-5 space-y-3 w-full ">
|
|
<View className="flex flex-col space-y-1 py-5 items-left">
|
|
<Text className="text-xl font-dmsans text-primary">
|
|
{t("selectacc.accountsTitle")}
|
|
</Text>
|
|
<Text className="text-base font-dmsans text-gray-400">
|
|
{selectedCardId
|
|
? t("selectacc.accountsDescriptionSelected")
|
|
: t("selectacc.accountsDescriptionUnselected")}
|
|
</Text>
|
|
</View>
|
|
<View className="flex flex-col items-left ">
|
|
<View className="flex flex-col items-center space-y-2 mt-3">
|
|
{renderCards()}
|
|
</View>
|
|
</View>
|
|
</ScrollView>
|
|
{selectedCardId && (
|
|
<View className="flex flex-row items-center space-x-2 w-full px-5 pb-6">
|
|
<Button
|
|
className="flex flex-row items-center space-x-2 bg-primary rounded-3xl p-3 w-full"
|
|
onPress={async () => {
|
|
if (!user?.uid || !amount || !selectedCardId) {
|
|
showToast(
|
|
t("selectacc.toastErrorTitle"),
|
|
t("selectacc.toastMissingInfo"),
|
|
"error"
|
|
);
|
|
return;
|
|
}
|
|
|
|
setIsProcessing(true);
|
|
try {
|
|
const selectedCard = wallet?.cards?.find(
|
|
(card) => card.id === selectedCardId
|
|
);
|
|
const amountInCents = parseInt(amount);
|
|
const currentBalance = wallet?.balance || 0;
|
|
const newBalance = currentBalance + amountInCents;
|
|
|
|
console.log(
|
|
"Processing transaction with card:",
|
|
selectedCard?.id,
|
|
"Amount:",
|
|
amountInCents,
|
|
"cents"
|
|
);
|
|
console.log(
|
|
"Current balance:",
|
|
currentBalance,
|
|
"Adding:",
|
|
amountInCents,
|
|
"New balance:",
|
|
newBalance
|
|
);
|
|
|
|
const result = await WalletService.updateWalletBalance(
|
|
user.uid,
|
|
newBalance
|
|
);
|
|
|
|
if (result.success) {
|
|
await refreshWallet();
|
|
|
|
router.replace({
|
|
pathname: ROUTES.ADDCASH_COMPLETION,
|
|
params: {
|
|
amount: (amountInCents / 100).toFixed(2),
|
|
},
|
|
});
|
|
} else {
|
|
showToast(
|
|
t("selectacc.toastErrorTitle"),
|
|
result.error || t("selectacc.toastAddCashFailed"),
|
|
"error"
|
|
);
|
|
}
|
|
} catch (error) {
|
|
console.error("Error adding cash:", error);
|
|
showToast(
|
|
t("selectacc.toastErrorTitle"),
|
|
t("selectacc.toastAddCashFailedWithRetry"),
|
|
"error"
|
|
);
|
|
} finally {
|
|
setIsProcessing(false);
|
|
}
|
|
}}
|
|
disabled={isProcessing}
|
|
>
|
|
<Text className="text-white text-base font-dmsans-medium">
|
|
{isProcessing
|
|
? t("selectacc.buttonProcessing")
|
|
: t("selectacc.buttonProceed")}
|
|
</Text>
|
|
</Button>
|
|
</View>
|
|
)}
|
|
<ModalToast
|
|
visible={toastVisible}
|
|
title={toastTitle}
|
|
description={toastDescription}
|
|
variant={toastVariant}
|
|
/>
|
|
</ScreenWrapper>
|
|
);
|
|
}
|