import { useLayoutEffect, useMemo, useState, useEffect, useCallback, } from "react"; import { useNavigation, useRouter } from "expo-router"; import { Animated, Easing, NativeScrollEvent, NativeSyntheticEvent, Pressable, ScrollView, StyleSheet, TouchableOpacity, } from "react-native"; import { Text, View } from "react-native"; import { useSelector } from "react-redux"; import { RootState } from "@/store"; import ProfileImage from "@/components/home/Profile"; import { Overlay } from "@/components/common/Overlay"; import { useSnackbar } from "@/contexts/Snackbar"; import CustomerCareIcon from "../../assets/icons/customer-care.svg"; import api from "@/services/axiosClient"; import PaymentHistoryCard from "@/components/Payments/PaymentHistoryCard"; import { BASE_URL } from "@/constants/config"; import { useDispatch } from "react-redux"; import { setDueAmount, setMyPlan } from "@/store/paymentSlice"; import { ActivityIndicator } from "react-native-paper"; import { useFocusEffect } from "@react-navigation/native"; import { displayValue } from "@/utils/Common"; import RefreshIcon from "@/assets/icons/refresh.svg"; import CustomerSupport from "@/components/home/CustomerSupportModal"; export interface MyPlan { no_of_emi: number; total_amount: number; down_payment: number; emi_amount: number; total_emi: number; installment_paid: number; current_amount: number; } interface EmiDetails { due_amount: number; total_amount_paid_in_current_cycle: number; due_date: string; status: string; advance_balance: number; pending_cycles: number; total_pending_installments: number; myPlain: MyPlan; } export interface EmiResponse { success: boolean; data: EmiDetails[]; } interface PaymentHistoryItem { id: number; amount: string; status: string; created_at: string; payment_mode: string[]; payment_date: string; } interface PaymentHistoryPagination { total_records: number; page_number: number; page_size: number; } interface PaymentHistoryResponse { success: boolean; data: { payments: PaymentHistoryItem[]; pagination: PaymentHistoryPagination; }; } function formatPaymentDate(input: string): string { const date = new Date(input); const options: Intl.DateTimeFormatOptions = { day: "2-digit", month: "short", year: "numeric", }; return date.toLocaleDateString("en-GB", options); } // Format time for payment history function formatPaymentTime(input: string): string { if (!input) return "--"; const date = new Date(input); const timeStr = date.toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit", hour12: true, }); const dayStr = date.toLocaleDateString("en-US", { weekday: "long" }); return `${timeStr} | ${dayStr}`; } export default function PaymentsTabScreen() { const navigation = useNavigation(); const { data } = useSelector((state: RootState) => state.user); const router = useRouter(); const { showSnackbar } = useSnackbar(); const [emiDetails, setEmiDetails] = useState(null); const [isLoading, setIsLoading] = useState(true); const [isSupportModalVisible, setIsSupportModalVisible] = useState(false); const [isEndReached, setIsEndReached] = useState(false); const dispatch = useDispatch(); //payment history states const [paymentHistory, setPaymentHistory] = useState( [] ); const [refreshing, setRefreshing] = useState(false); const spinValue = useState(new Animated.Value(0))[0]; const [isHistoryLoading, setIsHistoryLoading] = useState(false); const [showFullHistory, setShowFullHistory] = useState(false); const [currentPage, setCurrentPage] = useState(1); const [hasMorePages, setHasMorePages] = useState(true); const [isLoadingMore, setIsLoadingMore] = useState(false); const vehicle = Array.isArray(data?.vehicles) && data.vehicles.length > 0 ? data.vehicles[0] : null; const battery = Array.isArray(data?.batteries) && data.batteries.length > 0 ? data.batteries[0] : null; const model = vehicle?.model ?? "---"; const chasisNumber = vehicle?.chasis_number ?? "---"; const fetchEmiDetails = async () => { try { setIsLoading(true); setEmiDetails(null); const response = await api.get(`/api/v1/emi-details`); const result: EmiResponse = response.data; if (result.success && result.data.length > 0) { const details = result.data[0]; setEmiDetails(details); dispatch(setDueAmount(details.due_amount)); dispatch(setMyPlan(details.myPlain)); } else { showSnackbar("No EMI details found", "error"); } } catch (err) { console.error("Error fetching EMI details:", err); const errorMessage = err instanceof Error ? err.message : "Something went wrong"; showSnackbar(errorMessage, "error"); } finally { setIsLoading(false); } }; const handleRefresh = async () => { try { setShowFullHistory(false); setRefreshing(true); startSpin(); await Promise.all([fetchEmiDetails(), fetchPaymentHistory(1, false)]); console.log("Manual refresh complete"); } catch (error) { console.error("Manual refresh failed", error); } finally { stopSpin(); setRefreshing(false); } }; const startSpin = () => { spinValue.setValue(0); Animated.loop( Animated.timing(spinValue, { toValue: 1, duration: 1000, easing: Easing.linear, useNativeDriver: true, }) ).start(); }; const stopSpin = () => { spinValue.stopAnimation(); spinValue.setValue(0); }; const spin = spinValue.interpolate({ inputRange: [0, 1], outputRange: ["0deg", "360deg"], }); useFocusEffect( useCallback(() => { setShowFullHistory(false); fetchEmiDetails(); fetchPaymentHistory(1, false); }, []) ); useLayoutEffect(() => { navigation.setOptions({ headerStyle: { backgroundColor: "#F3F5F8", }, headerTitle: () => ( {model} {chasisNumber} ), headerRight: () => ( handleRefresh()} disabled={refreshing}> { console.log("Support Pressed"); setIsSupportModalVisible(true); }} > { router.push("/user/profile"); }} > router.push("/user/profile")} textSize={20} boxSize={40} /> ), }); }, [navigation, data, model, chasisNumber, refreshing]); // Format currency const formatCurrency = (amount: number) => { return `₹${amount.toLocaleString()}`; }; const handleViewAll = () => { setShowFullHistory(true); }; // Format date const formatDate = (dateString: string) => { try { const date = new Date(dateString); return date.toLocaleDateString("en-IN", { day: "2-digit", month: "short", year: "numeric", }); } catch { return dateString; } }; // Get current month/year for header const getCurrentMonthYear = () => { const date = new Date(); return date.toLocaleDateString("en-US", { month: "long", year: "numeric", }); }; const fetchPaymentHistory = async ( pageNumber: number = 1, isLoadMore: boolean = false ) => { try { if (isLoadMore) { setIsLoadingMore(true); } else { setIsHistoryLoading(true); } const response = await api.get( `${BASE_URL}/api/v1/payment-history?page_number=${pageNumber}&page_size=10` ); const result: PaymentHistoryResponse = response.data; console.log("Payment History Response:", result); if (result.success) { const newPayments = result.data.payments; if (isLoadMore) { setPaymentHistory((prev) => [...prev, ...newPayments]); } else { setPaymentHistory(newPayments); } // Check if there are more pages const totalPages = Math.ceil( result.data.pagination.total_records / result.data.pagination.page_size ); setHasMorePages(pageNumber < totalPages); setCurrentPage(pageNumber); } else { showSnackbar("No payment history found", "error"); } } catch (err) { console.error("Error fetching payment history:", err); const errorMessage = err instanceof Error ? err.message : "Something went wrong"; showSnackbar(errorMessage, "error"); } finally { if (isLoadMore) { setIsLoadingMore(false); } else { setIsHistoryLoading(false); } } }; const handleLoadMore = () => { if (hasMorePages && !isLoadingMore) { fetchPaymentHistory(currentPage + 1, true); } }; useEffect(() => { if (isEndReached && showFullHistory && hasMorePages && !isLoadingMore) { handleLoadMore(); } }, [isEndReached]); const handleScroll = (event: NativeSyntheticEvent) => { const { layoutMeasurement, contentOffset, contentSize } = event.nativeEvent; const paddingToBottom = 20; const isAtBottom = layoutMeasurement.height + contentOffset.y >= contentSize.height - paddingToBottom; setIsEndReached(isAtBottom); }; return ( <> {/* Last EMI Details Card */} {/* Header */} Last EMI Details {getCurrentMonthYear()} {/* Divider */} {/* EMI Details Content */} Amount Due {displayValue(emiDetails?.due_amount, formatCurrency)} Amount Paid {displayValue( emiDetails?.total_amount_paid_in_current_cycle, formatCurrency )} Due Date {displayValue(emiDetails?.due_date)} Payment Status {emiDetails?.status ? ( ) : ( -- )} router.push("/payments/selectAmount")} > Pay EMI router.push("/payments/myPlan")} > View Plan Payment History {isHistoryLoading ? ( ) : ( <> {/* Show initial payments or all payments based on showFullHistory */} {(showFullHistory ? paymentHistory : paymentHistory.slice(0, 3) ).map((payment) => ( ))} {!showFullHistory && paymentHistory.length > 2 && ( View all )} {isLoadingMore && ( )} {showFullHistory && !hasMorePages && paymentHistory.length > 0 && ( No more payments to show )} {/* Empty state */} {paymentHistory.length === 0 && !isHistoryLoading && ( No payment history found )} )} setIsSupportModalVisible(false)} /> ); } const StatusBadge = ({ label, type, }: { label: string | undefined; type: string | undefined; }) => { if (!label || !type) return "--"; const getBadgeStyle = (type: string) => { switch (type) { case "pending": return { backgroundColor: "#FFF0E3", color: "#8E4400", }; case "completed": return { backgroundColor: "#E8F5E8", color: "#2D7D32", }; default: return { backgroundColor: "#E8F5E8", color: "#2D7D32", }; } }; const badgeStyle = getBadgeStyle(type); return ( {label.charAt(0).toUpperCase() + label.slice(1)} ); }; const styles = StyleSheet.create({ loadingContainer: { padding: 16, alignItems: "center", justifyContent: "center", }, container: { flex: 1, alignItems: "center", justifyContent: "center", }, title: { fontSize: 14, color: "#6B7280", fontWeight: "500", }, separator: { marginVertical: 30, height: 1, width: "80%", }, headerTitleContainer: { flexDirection: "column", backgroundColor: "#F3F5F8", }, subtitle: { fontSize: 18, color: "#111827", fontWeight: "700", }, rightContainer: { flexDirection: "row", alignItems: "center", paddingRight: 16, gap: 8, backgroundColor: "#F3F5F8", }, supportButton: { backgroundColor: "#F3F5F8", }, scrollContainer: { flex: 1, paddingHorizontal: 16, paddingBottom: 16, width: "100%", }, // EMI Card Styles emiCard: { backgroundColor: "#FCFCFC", borderRadius: 8, padding: 12, marginBottom: 36, }, cardHeader: { flexDirection: "row", justifyContent: "space-between", alignItems: "flex-end", marginBottom: 12, }, headerTitle: { fontSize: 14, fontWeight: "600", color: "#252A34", }, headerDate: { fontSize: 14, fontWeight: "500", color: "#565C70", }, divider: { height: 1, backgroundColor: "#E5E9F0", marginBottom: 12, }, cardContent: { gap: 8, marginBottom: 12, }, detailRow: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", minHeight: 20, }, detailLabel: { fontSize: 14, color: "#252A34", flex: 1, }, detailValue: { fontSize: 14, fontWeight: "600", color: "#252A34", }, // Badge Styles badge: { paddingHorizontal: 8, paddingVertical: 2, borderRadius: 4, }, badgeText: { fontSize: 12, fontWeight: "500", }, // Button Styles buttonContainer: { gap: 8, }, primaryButton: { backgroundColor: "#008761", borderRadius: 4, paddingVertical: 8, paddingHorizontal: 16, alignItems: "center", minHeight: 40, justifyContent: "center", }, primaryButtonText: { color: "#FCFCFC", fontSize: 14, fontWeight: "500", }, tertiaryButton: { borderRadius: 4, paddingVertical: 8, alignItems: "center", minHeight: 36, justifyContent: "center", }, tertiaryButtonText: { color: "#006C4D", fontSize: 14, fontWeight: "500", }, // Plan Details Styles planDetailsSection: { gap: 8, marginBottom: 20, }, planCard: { backgroundColor: "#FCFCFC", borderRadius: 8, padding: 12, }, sectionTitle: { fontSize: 14, lineHeight: 20, color: "#252A34", // rgb(37,42,51) from design marginBottom: 8, fontWeight: "600", }, paymentHistoryContainer: { flexDirection: "column", gap: 16, }, historyLoadingContainer: { height: 312, justifyContent: "center", alignItems: "center", }, loadingText: { fontFamily: "Inter-Regular", fontSize: 14, color: "#252A33", }, noDataContainer: { height: 312, justifyContent: "center", alignItems: "center", }, noDataText: { fontFamily: "Inter-Regular", fontSize: 14, color: "#252A33", }, noMoreDataContainer: { marginTop: 16, justifyContent: "center", alignItems: "center", }, noMoreDataText: { fontFamily: "Inter-Regular", fontSize: 12, color: "#5B6478", // rgb(91,100,120) muted text }, paymentCard: { width: 328, height: 76, backgroundColor: "#FCFCFC", // #FCFCFC or #FBFBFB as in design (near white) borderRadius: 8, paddingVertical: 12, paddingHorizontal: 12, marginBottom: 16, justifyContent: "space-between", // flexDirection: "column" by default }, paymentCardTopRow: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", height: 20, }, paymentDate: { fontFamily: "Inter-Regular", fontSize: 14, color: "#252A33", }, paymentAmount: { fontFamily: "Inter-SemiBold", fontSize: 14, color: "#252A33", }, paymentCardBottomRow: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", height: 20, }, paymentTimeMethod: { fontFamily: "Inter-Regular", fontSize: 12, color: "#56607A", // rgb(86,96,122) }, paymentStatusBadge: { borderRadius: 4, paddingVertical: 2, paddingHorizontal: 8, flexDirection: "row", alignItems: "center", justifyContent: "center", }, paymentStatusLabel: { fontFamily: "Inter-Medium", fontSize: 12, textAlign: "center", }, // Status colors — you can override backgroundColor and color dynamically based on status statusPending: { backgroundColor: "#FFF0E5", // approx. (1, 0.941, 0.89) color: "#803F0C", // approx. (0.5, 0.27, 0.047) }, statusFailed: { backgroundColor: "#FDDDD7", // approx. (0.99, 0.91, 0.9) color: "#D6290A", // approx. (0.83, 0.11, 0.06) }, statusSuccess: { backgroundColor: "#E6F4EA", // light green example color: "#0B8235", }, viewAllButton: { flexDirection: "row", justifyContent: "center", alignItems: "center", paddingVertical: 8, paddingHorizontal: 16, borderRadius: 4, }, viewAllText: { fontFamily: "Inter-Medium", fontSize: 14, color: "#007958", // greenish text color }, chevron: { fontSize: 18, color: "#007958", marginLeft: 4, }, loadMoreButton: { paddingVertical: 8, paddingHorizontal: 16, borderRadius: 4, backgroundColor: "#007958", alignSelf: "center", }, loadMoreText: { fontFamily: "Inter-Medium", fontSize: 14, color: "#FFFFFF", }, });