import React, { useState, useEffect } from "react"; import { View, ScrollView, Keyboard, TouchableOpacity, Platform, } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import { ArrowLeftIcon, ChevronLeft, ChevronRight } 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 Dropdown from "~/components/ui/dropdown"; import { router } from "expo-router"; import BackButton from "~/components/ui/backButton"; import { useRecipientsStore } from "~/lib/stores"; import { useTabStore } from "~/lib/stores"; import { ROUTES } from "~/lib/routes"; import { formatPhoneNumber } from "~/lib/utils/phoneUtils"; import ScreenWrapper from "~/components/ui/ScreenWrapper"; import ModalToast from "~/components/ui/toast"; import { useTranslation } from "react-i18next"; import { awardPoints } from "~/lib/services/pointsService"; import BottomSheet from "~/components/ui/bottomSheet"; import { Picker } from "react-native-wheel-pick"; const PROFILE_BANK_OPTIONS: { id: string; name: string }[] = [ { id: "cbe", name: "Commercial Bank of Ethiopia" }, { id: "dashen", name: "Dashen Bank" }, { id: "abay", name: "Abay Bank" }, { id: "awash", name: "Awash Bank" }, { id: "hibret", name: "Hibret Bank" }, { id: "telebirr", name: "Ethio Telecom (Telebirr)" }, { id: "safaricom", name: "Safaricom M-PESA" }, ]; export default function AddRecpi() { const { addRecipient, loading, addError, clearAddError } = useRecipientsStore(); const { setLastVisitedTab } = useTabStore(); const [fullName, setFullName] = useState(""); const [phoneNumber, setPhoneNumber] = useState(""); const [isSubmitting, setIsSubmitting] = useState(false); const [hasAttemptedSubmit, setHasAttemptedSubmit] = useState(false); const [clientType, setClientType] = useState<"Individual" | "Business">( "Individual" ); const [selectedBank, setSelectedBank] = useState(null); const [accountNumberInput, setAccountNumberInput] = useState(""); const [accountSheetVisible, setAccountSheetVisible] = useState(false); const [scheduleSheetVisible, setScheduleSheetVisible] = useState(false); const [scheduleFrequency, setScheduleFrequency] = useState(""); const [scheduleHour, setScheduleHour] = useState("00"); const [scheduleMinute, setScheduleMinute] = useState("00"); const [schedulePeriod, setSchedulePeriod] = useState<"AM" | "PM">("AM"); const [scheduleTime, setScheduleTime] = useState(""); const [scheduleDate, setScheduleDate] = useState(""); const { t } = useTranslation(); const isTelecomWallet = selectedBank === "telebirr" || selectedBank === "safaricom"; const accountLabel = isTelecomWallet ? "Phone Number" : "Account Number"; const accountPlaceholder = isTelecomWallet ? "Enter phone number" : "Enter account number"; // Time wheel options const HOURS = Array.from({ length: 24 }, (_, i) => String(i).padStart(2, "0") ); const MINUTES = Array.from({ length: 60 }, (_, i) => String(i).padStart(2, "0") ); const PERIODS: ("AM" | "PM")[] = ["AM", "PM"]; // Calendar state for month navigation (UI-only) const today = new Date(); const [calendarCursor, setCalendarCursor] = useState(() => { const start = new Date(); start.setDate(1); return start; }); const cursorYear = calendarCursor.getFullYear(); const cursorMonth = calendarCursor.getMonth(); const firstOfMonth = new Date(cursorYear, cursorMonth, 1); const lastOfMonth = new Date(cursorYear, cursorMonth + 1, 0); const firstWeekday = firstOfMonth.getDay(); // 0 = Sun const calendarDays = [ // Leading empty slots before the 1st ...Array.from({ length: firstWeekday }, () => null), // Actual days ...Array.from({ length: lastOfMonth.getDate() }, (_, idx) => { const day = idx + 1; const key = `${cursorYear}-${cursorMonth + 1}-${day}`; const isToday = today.getFullYear() === cursorYear && today.getMonth() === cursorMonth && today.getDate() === day; return { key, label: String(day), isToday }; }), ]; const MONTH_NAMES = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ]; 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 ); // Keep a combined time string for potential future summary usage useEffect(() => { setScheduleTime(`${scheduleHour}:${scheduleMinute} ${schedulePeriod}`); }, [scheduleHour, scheduleMinute, schedulePeriod]); 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 useEffect(() => { setLastVisitedTab("/(tabs)/listrecipient"); }, [setLastVisitedTab]); useEffect(() => { return () => { if (toastTimeoutRef.current) { clearTimeout(toastTimeoutRef.current); } }; }, []); const handlePhoneChange = (value: string) => { const formatted = formatPhoneNumber(value); setPhoneNumber(formatted); }; const validateForm = () => { if (!fullName.trim()) { showToast( t("addrecipient.validationErrorTitle"), t("addrecipient.validationFullNameRequired"), "error" ); return false; } if (!phoneNumber.trim()) { showToast( t("addrecipient.validationErrorTitle"), t("addrecipient.validationPhoneRequired"), "error" ); return false; } const cleanPhone = phoneNumber.replace(/[^+\d]/g, ""); if (cleanPhone.length < 7) { showToast( t("addrecipient.validationErrorTitle"), t("addrecipient.validationPhoneInvalid"), "error" ); return false; } return true; }; const handleAddRecipient = async () => { Keyboard.dismiss(); if (!validateForm()) return; setIsSubmitting(true); setHasAttemptedSubmit(true); await addRecipient({ fullName: fullName.trim(), phoneNumber: phoneNumber.trim(), }); setIsSubmitting(false); }; // Show error if any useEffect(() => { if (addError) { showToast( t("addrecipient.toastErrorTitle"), addError || t("addrecipient.toastAddError"), "error" ); // Clear the error after showing it clearAddError(); // Reset submission state setHasAttemptedSubmit(false); } }, [addError, clearAddError]); // Show success when operation completes without error useEffect(() => { if (hasAttemptedSubmit && !loading && !addError && !isSubmitting) { // Operation completed successfully router.replace(ROUTES.RECIPIENT_ADDED); awardPoints("add_recipient").catch((error) => { console.warn( "[AddRecipient] Failed to award add recipient points", error ); }); } }, [loading, addError, isSubmitting, hasAttemptedSubmit]); const isFormValid = fullName.trim() && phoneNumber.trim() && phoneNumber.replace(/[^+\d]/g, "").length >= 7; return ( {/* Header */} {t("addrecipient.title")} {t("addrecipient.sectionSubtitle")} {/* Client details card */} {/* Client type toggle */} {t("addrecipient.clientTypeLabel", "Client type")} {(["Individual", "Business"] as const).map((type) => { const isActive = clientType === type; return ( setClientType(type)} > {type} ); })} {/* Actions for account & schedule (UI-only) */} {/* Bottom sheet: Add Account (UI-only, matches Edit Profile add account) */} setAccountSheetVisible(false)} maxHeightRatio={0.9} > {t("addrecipient.accountSheetTitle", "Add Account")} {t("addrecipient.accountBankLabel", "Bank")} {PROFILE_BANK_OPTIONS.map((bank) => { const isSelected = selectedBank === bank.id; const initials = bank.name .split(" ") .map((part) => part[0]) .join("") .toUpperCase() .slice(0, 2); return ( setSelectedBank(bank.id)} className={`items-center justify-between px-3 py-4 mb-3 rounded-2xl border ${ isSelected ? "border-primary bg-primary/5" : "border-gray-200 bg-white" }`} style={{ width: "30%" }} > {initials} {bank.name} ); })} {accountLabel} setAccountNumberInput(text.replace(/[^0-9]/g, "")) } containerClassName="w-full mb-4" borderClassName="border-[#E5E7EB] bg-white rounded-[4px]" placeholderColor="#9CA3AF" textClassName="text-[#111827] text-sm" keyboardType="number-pad" /> {/* Bottom sheet: Set Schedule (UI-only with dropdowns & calendar) */} setScheduleSheetVisible(false)} maxHeightRatio={0.95} > {t("addrecipient.scheduleSheetTitle", "Set Schedule")} {t( "addrecipient.scheduleSheetSubtitle", "Choose a simple reminder schedule for this client. This is UI-only for now." )} {/* Frequency dropdown */} setScheduleFrequency(val)} placeholder={t( "addrecipient.scheduleFrequencyPlaceholder", "Daily / Weekly / Monthly / Custom" )} /> {/* Time-of-day selector: 3-column wheel (hour, minute, AM/PM) */} {/* Hour wheel */} {t("addrecipient.scheduleHourLabel", "Hour")} setScheduleHour(val)} textColor="#9CA3AF" selectTextColor="#16A34A" isCyclic /> {/* Minute wheel */} {t("addrecipient.scheduleMinuteLabel", "Min")} setScheduleMinute(val)} textColor="#9CA3AF" selectTextColor="#16A34A" isCyclic /> {/* AM/PM wheel */} {t("addrecipient.schedulePeriodLabel", "AM/PM")} setSchedulePeriod(val as "AM" | "PM") } textColor="#9CA3AF" selectTextColor="#16A34A" /> {/* Date selector - calendar with header & weekdays */} {/* Month header */} { setCalendarCursor( (prev) => new Date(prev.getFullYear(), prev.getMonth() - 1, 1) ); }} className="p-1" > {MONTH_NAMES[cursorMonth]} {cursorYear} { setCalendarCursor( (prev) => new Date(prev.getFullYear(), prev.getMonth() + 1, 1) ); }} className="p-1" > {/* Weekday labels */} {["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].map((d) => ( {d} ))} {/* Day grid: 7 columns (Sun-Sat) */} {Array.from( { length: Math.ceil(calendarDays.length / 7) }, (_, rowIndex) => { const row = calendarDays.slice( rowIndex * 7, rowIndex * 7 + 7 ); return ( {row.map((day, idx) => { if (!day) { return ( ); } const safeDay = day as { key: string; label: string; isToday: boolean; }; const isSelected = scheduleDate === safeDay.key; const isToday = safeDay.isToday; return ( setScheduleDate(safeDay.key)} className={`items-center justify-center rounded-full border ${ isSelected ? "bg-primary border-primary" : "bg-white border-transparent" }`} style={{ width: 32, height: 32 }} > {safeDay.label} {isToday && !isSelected && ( {t( "addrecipient.scheduleTodayLabel", "Today" )} )} ); })} ); } )} ); }