import React, { useState, useEffect } from "react"; import { ScrollView, View, TouchableOpacity, Keyboard } from "react-native"; import { LucideEye, EyeOff, Trash2, CreditCard } from "lucide-react-native"; import { Button } from "~/components/ui/button"; import { Input } from "~/components/ui/input"; import { Label } from "~/components/ui/label"; import { Text } from "~/components/ui/text"; import { router, useLocalSearchParams } from "expo-router"; import { useAuthWithProfile } from "~/lib/hooks/useAuthWithProfile"; import { useUserWallet } from "~/lib/hooks/useUserWallet"; import { CreditCard as CreditCardType } from "~/lib/services/walletService"; import { useTabStore } from "~/lib/stores"; import { addCardSchema, validate } from "~/lib/utils/validationSchemas"; import BackButton from "~/components/ui/backButton"; import { ROUTES } from "~/lib/routes"; import ScreenWrapper from "~/components/ui/ScreenWrapper"; import ModalToast from "~/components/ui/toast"; import { useTranslation } from "react-i18next"; // Credit Card Component const CreditCardComponent = ({ card, onRemove, }: { card: CreditCardType; onRemove: () => void; }) => { const getCardColor = (cardType: string) => { switch (cardType?.toLowerCase()) { case "visa": return "bg-blue-700"; case "mastercard": return "bg-red-600"; case "american express": return "bg-green-700"; case "discover": return "bg-orange-600"; default: return "bg-gray-700"; } }; const getCardIcon = (cardType: string) => { switch (cardType?.toLowerCase()) { case "visa": return "VISA"; case "mastercard": return "MC"; case "american express": return "AMEX"; case "discover": return "DISC"; default: return "CARD"; } }; return ( {/* Card Brand and Remove Button */} {getCardIcon(card.cardType || "")} { console.log("Remove button pressed for card:", card.id); onRemove(); }} className="bg-gray-500/80 p-3 rounded-full min-w-10 min-h-10 flex items-center justify-center" activeOpacity={0.7} style={{ zIndex: 10 }} > {/* Card Number */} {card.cardNumber} {/* Card Details */} Valid Thru {card.expiryDate} Card Type {card.cardType || "Unknown"} {/* Decorative Elements */} ); }; export default function AddCard() { const { user } = useAuthWithProfile(); const { addCreditCard, loading, error } = useUserWallet(user); const { setLastVisitedTab } = useTabStore(); const { from } = useLocalSearchParams<{ from?: string }>(); const { t } = useTranslation(); const [toastVisible, setToastVisible] = useState(false); const [toastTitle, setToastTitle] = useState(""); const [toastDescription, setToastDescription] = useState( undefined ); const [toastVariant, setToastVariant] = useState< "success" | "error" | "warning" | "info" >("info"); const toastTimeoutRef = React.useRef | 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); }; // Set the tab state when component mounts based on where user came from useEffect(() => { if (from === "addcash") { setLastVisitedTab("/(tabs)/cardmang"); } else { setLastVisitedTab("/"); // Default to home if no specific from parameter } }, [setLastVisitedTab, from]); useEffect(() => { return () => { if (toastTimeoutRef.current) { clearTimeout(toastTimeoutRef.current); } }; }, []); // Form state const [cardNumber, setCardNumber] = useState(""); const [expiryDate, setExpiryDate] = useState(""); const [cvv, setCvv] = useState(""); const [showCvv, setShowCvv] = useState(false); // Format card number as user types (add spaces every 4 digits) const formatCardNumber = (value: string) => { const v = value.replace(/\s+/g, "").replace(/[^0-9]/gi, ""); const matches = v.match(/\d{4,16}/g); const match = (matches && matches[0]) || ""; const parts = []; for (let i = 0, len = match.length; i < len; i += 4) { parts.push(match.substring(i, i + 4)); } if (parts.length) { return parts.join(" "); } else { return v; } }; // Format expiry date as MM/YY const formatExpiryDate = (value: string) => { const v = value.replace(/\s+/g, "").replace(/[^0-9]/gi, ""); if (v.length >= 2) { return `${v.substring(0, 2)}/${v.substring(2, 4)}`; } return v; }; const handleCardNumberChange = (value: string) => { const formatted = formatCardNumber(value); if (formatted.length <= 19) { // 16 digits + 3 spaces setCardNumber(formatted); } }; const handleExpiryDateChange = (value: string) => { const formatted = formatExpiryDate(value); if (formatted.length <= 5) { // MM/YY setExpiryDate(formatted); } }; const handleCvvChange = (value: string) => { const v = value.replace(/[^0-9]/gi, ""); if (v.length <= 4) { setCvv(v); } }; const handleAddCard = async () => { Keyboard.dismiss(); // Basic field validation if (!cardNumber.trim()) { showToast( t("addcard.validationErrorTitle"), t("addcard.validationCardNumberRequired"), "error" ); return; } if (!expiryDate.trim()) { showToast( t("addcard.validationErrorTitle"), t("addcard.validationExpiryRequired"), "error" ); return; } if (!cvv.trim()) { showToast( t("addcard.validationErrorTitle"), t("addcard.validationCvvRequired"), "error" ); return; } // Validate form using valibot const validationResult = validate(addCardSchema, { cardNumber, expiryDate, cvv, }); if (!validationResult.success) { showToast( t("addcard.validationErrorTitle"), validationResult.error || t("addcard.validationInvalidCard"), "error" ); return; } try { await addCreditCard({ cardNumber: validationResult.data.cardNumber.replace(/\s/g, ""), // Remove spaces expiryDate: validationResult.data.expiryDate, cvv: validationResult.data.cvv, }); router.replace(ROUTES.CARD_ADDED); } catch (err) { showToast( t("addcard.toastErrorTitle"), t("addcard.toastAddFailed"), "error" ); } }; // Show errors useEffect(() => { if (error) { showToast(t("addcard.toastErrorTitle"), error, "error"); } }, [error]); return ( {t("addcard.title")} {t("addcard.sectionCardTitle")} {t("addcard.sectionCardSubtitle")} setShowCvv(!showCvv)}> {showCvv ? ( ) : ( )} } /> ); }